memorix 1.9.4 → 1.9.6
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 +75 -1
- package/README.md +12 -6
- package/README.zh-CN.md +12 -6
- package/dist/cli/index.js +2533 -585
- package/dist/index.d.ts +15 -8
- package/dist/index.js +1201 -284
- package/dist/maintenance-runner.js +387 -216
- package/dist/media-audio-runner.js +300 -129
- package/dist/media-video-runner.js +300 -129
- package/dist/memcode-runtime/CHANGELOG.md +75 -1
- package/dist/sdk.d.ts +1 -0
- package/dist/sdk.js +1882 -998
- package/dist/types.d.ts +1 -1
- package/dist/vector-backfill-runner.js +300 -129
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +47 -3
- package/docs/API_REFERENCE.md +39 -1
- package/docs/CONFIGURATION.md +58 -1
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/DOCKER.md +23 -3
- package/docs/INTEGRATIONS.md +1 -1
- package/docs/KNOWN_ISSUES_AND_ROADMAP.md +61 -1
- package/docs/MULTI-DEVICE-SYNC-RESEARCH.md +4 -0
- package/docs/PERFORMANCE.md +46 -2
- package/docs/README.md +2 -1
- package/docs/SETUP.md +56 -1
- package/package.json +3 -2
- package/plugins/antigravity/memorix/plugin.json +1 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +1 -1
- package/plugins/hermes/memorix/plugin.yaml +1 -1
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/cli/capability-map.ts +1 -0
- package/src/cli/command-guide.ts +15 -3
- package/src/cli/commands/agent-integrations.ts +57 -0
- package/src/cli/commands/background.ts +11 -2
- package/src/cli/commands/context.ts +2 -0
- package/src/cli/commands/continuity.ts +155 -0
- package/src/cli/commands/init.ts +5 -2
- package/src/cli/commands/serve-http.ts +435 -84
- package/src/cli/commands/setup.ts +77 -10
- package/src/cli/commands/sync-store.ts +47 -12
- package/src/cli/commands/sync.ts +26 -0
- package/src/cli/index.ts +3 -1
- package/src/cli/tui/ConfigureView.tsx +2 -0
- package/src/codegraph/auto-context.ts +17 -0
- package/src/config/legacy-loader.ts +2 -2
- package/src/config/resolved-config.ts +8 -5
- package/src/config/yaml-loader.ts +20 -10
- package/src/dashboard/server.ts +10 -12
- package/src/embedding/api-provider.ts +4 -2
- package/src/embedding/fastembed-provider.ts +47 -14
- package/src/embedding/transformers-provider.ts +29 -7
- package/src/evaluation/memory-quality-gate.ts +90 -0
- package/src/hooks/official-skills.ts +2 -0
- package/src/integrations/registry.ts +1 -1
- package/src/knowledge/context-assembly.ts +1 -0
- package/src/knowledge/context-receipt.ts +15 -0
- package/src/knowledge/task-continuity.ts +413 -0
- package/src/knowledge/workset.ts +77 -0
- package/src/llm/provider.ts +2 -1
- package/src/memory/graph.ts +10 -8
- package/src/memory/session.ts +1 -1
- package/src/orchestrate/coordinator.ts +60 -0
- package/src/orchestrate/effect-ledger.ts +160 -0
- package/src/orchestrate/pipeline-trace.ts +40 -6
- package/src/runtime/maintenance-jobs.ts +12 -6
- package/src/sdk.ts +24 -0
- package/src/server/mcp-binding-store.ts +14 -3
- package/src/server/modern-mcp-bridge.ts +153 -27
- package/src/server/tool-profile.ts +3 -2
- package/src/server.ts +131 -7
- package/src/store/file-lock.ts +28 -3
- package/src/store/graph-store.ts +178 -46
- package/src/store/persistence.ts +14 -9
- package/src/store/sqlite-db.ts +150 -7
- package/src/store/sqlite-store.ts +32 -27
- package/src/sync/engine.ts +9 -7
- package/src/sync/namespace.ts +23 -0
- package/src/sync/policy.ts +19 -4
- package/src/sync/store-port.ts +43 -13
- package/src/sync/types.ts +2 -0
- package/src/team/team-store.ts +62 -27
- package/src/types.ts +1 -1
- package/src/workspace/mcp-adapters/grok.ts +32 -0
|
@@ -99,7 +99,8 @@ function evictOldest(): void {
|
|
|
99
99
|
if (old) cachePayloadBytes -= payloadBytes(old);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
function evictToFit(incomingBytes: number, incomingDims: number):
|
|
102
|
+
function evictToFit(incomingBytes: number, incomingDims: number): boolean {
|
|
103
|
+
if (incomingBytes > maxCachePayloadBytes()) return false;
|
|
103
104
|
const limit = maxEntriesForDimensions(incomingDims);
|
|
104
105
|
while (
|
|
105
106
|
cache.size > 0 &&
|
|
@@ -107,6 +108,7 @@ function evictToFit(incomingBytes: number, incomingDims: number): void {
|
|
|
107
108
|
) {
|
|
108
109
|
evictOldest();
|
|
109
110
|
}
|
|
111
|
+
return true;
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
function isNumberArray(value: unknown): value is number[] {
|
|
@@ -139,7 +141,7 @@ function cacheInsert(hash: string, value: number[], dirty: boolean): void {
|
|
|
139
141
|
cachePayloadBytes -= payloadBytes(existing);
|
|
140
142
|
cache.delete(hash);
|
|
141
143
|
}
|
|
142
|
-
evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS);
|
|
144
|
+
if (!evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS)) return;
|
|
143
145
|
cache.set(hash, value);
|
|
144
146
|
cachePayloadBytes += incoming;
|
|
145
147
|
if (dirty) diskCacheDirty = true;
|
|
@@ -30,6 +30,9 @@ const CACHE_FILE = join(CACHE_DIR, '.embedding-cache.json');
|
|
|
30
30
|
// In-memory cache keyed by text hash → embedding
|
|
31
31
|
const cache = new Map<string, number[]>();
|
|
32
32
|
const MAX_CACHE_SIZE = 5000;
|
|
33
|
+
const DEFAULT_CACHE_PAYLOAD_BYTES = 64 * 1024 * 1024;
|
|
34
|
+
const MAX_CACHE_FILE_BYTES = 128 * 1024 * 1024;
|
|
35
|
+
let cachePayloadBytes = 0;
|
|
33
36
|
let diskCacheDirty = false;
|
|
34
37
|
|
|
35
38
|
function textHash(text: string): string {
|
|
@@ -39,14 +42,53 @@ function textHash(text: string): string {
|
|
|
39
42
|
async function loadDiskCache(): Promise<void> {
|
|
40
43
|
try {
|
|
41
44
|
const raw = await readFile(CACHE_FILE, 'utf-8');
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
if (Buffer.byteLength(raw, 'utf8') > MAX_CACHE_FILE_BYTES) {
|
|
46
|
+
console.error('[memorix] FastEmbed cache exceeds the disk load limit; starting with an empty cache');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const entries: unknown = JSON.parse(raw);
|
|
50
|
+
if (!Array.isArray(entries)) return;
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
if (!Array.isArray(entry) || typeof entry[0] !== 'string' || !isVector(entry[1])) continue;
|
|
53
|
+
cacheSet(entry[0], entry[1], false);
|
|
54
|
+
}
|
|
55
|
+
console.error(`[memorix] Loaded ${cache.size} cached embeddings from disk`);
|
|
45
56
|
} catch {
|
|
46
57
|
// No cache file or corrupt — start fresh
|
|
47
58
|
}
|
|
48
59
|
}
|
|
49
60
|
|
|
61
|
+
function isVector(value: unknown): value is number[] {
|
|
62
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === 'number');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function maxCachePayloadBytes(): number {
|
|
66
|
+
const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? '', 10);
|
|
67
|
+
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function cacheSet(hash: string, value: number[], markDirty = true): void {
|
|
71
|
+
const incomingBytes = value.length * 8;
|
|
72
|
+
if (incomingBytes > maxCachePayloadBytes()) return;
|
|
73
|
+
const existing = cache.get(hash);
|
|
74
|
+
if (existing) cachePayloadBytes -= existing.length * 8;
|
|
75
|
+
else {
|
|
76
|
+
while (
|
|
77
|
+
cache.size >= MAX_CACHE_SIZE
|
|
78
|
+
|| cachePayloadBytes + incomingBytes > maxCachePayloadBytes()
|
|
79
|
+
) {
|
|
80
|
+
const firstKey = cache.keys().next().value;
|
|
81
|
+
if (firstKey === undefined) break;
|
|
82
|
+
const old = cache.get(firstKey);
|
|
83
|
+
cache.delete(firstKey);
|
|
84
|
+
if (old) cachePayloadBytes -= old.length * 8;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
cache.set(hash, value);
|
|
88
|
+
cachePayloadBytes += incomingBytes;
|
|
89
|
+
if (markDirty) diskCacheDirty = true;
|
|
90
|
+
}
|
|
91
|
+
|
|
50
92
|
async function saveDiskCache(): Promise<void> {
|
|
51
93
|
if (!diskCacheDirty) return;
|
|
52
94
|
try {
|
|
@@ -109,7 +151,7 @@ export class FastEmbedProvider implements EmbeddingProvider {
|
|
|
109
151
|
if (result.length !== this.dimensions) {
|
|
110
152
|
throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
|
|
111
153
|
}
|
|
112
|
-
|
|
154
|
+
cacheSet(hash, result);
|
|
113
155
|
return result;
|
|
114
156
|
}
|
|
115
157
|
|
|
@@ -139,7 +181,7 @@ export class FastEmbedProvider implements EmbeddingProvider {
|
|
|
139
181
|
const originalIdx = uncachedIndices[batchIdx];
|
|
140
182
|
const plain = Array.from(vec) as number[];
|
|
141
183
|
results[originalIdx] = plain;
|
|
142
|
-
|
|
184
|
+
cacheSet(textHash(uncachedTexts[batchIdx]), plain);
|
|
143
185
|
batchIdx++;
|
|
144
186
|
}
|
|
145
187
|
}
|
|
@@ -150,13 +192,4 @@ export class FastEmbedProvider implements EmbeddingProvider {
|
|
|
150
192
|
return results;
|
|
151
193
|
}
|
|
152
194
|
|
|
153
|
-
private cacheSet(hash: string, value: number[]): void {
|
|
154
|
-
// Evict oldest entries if cache is full
|
|
155
|
-
if (cache.size >= MAX_CACHE_SIZE) {
|
|
156
|
-
const firstKey = cache.keys().next().value;
|
|
157
|
-
if (firstKey !== undefined) cache.delete(firstKey);
|
|
158
|
-
}
|
|
159
|
-
cache.set(hash, value);
|
|
160
|
-
diskCacheDirty = true;
|
|
161
|
-
}
|
|
162
195
|
}
|
|
@@ -23,10 +23,22 @@ import {
|
|
|
23
23
|
type EmbeddingOptions,
|
|
24
24
|
type EmbeddingProvider,
|
|
25
25
|
} from './provider.js';
|
|
26
|
+
import { createHash } from 'node:crypto';
|
|
26
27
|
|
|
27
28
|
// In-memory LRU cache
|
|
28
29
|
const cache = new Map<string, number[]>();
|
|
29
30
|
const MAX_CACHE_SIZE = 5000;
|
|
31
|
+
const DEFAULT_CACHE_PAYLOAD_BYTES = 64 * 1024 * 1024;
|
|
32
|
+
let cachePayloadBytes = 0;
|
|
33
|
+
|
|
34
|
+
function cacheKey(text: string): string {
|
|
35
|
+
return createHash('sha256').update(text).digest('hex');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function maxCachePayloadBytes(): number {
|
|
39
|
+
const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? '', 10);
|
|
40
|
+
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES;
|
|
41
|
+
}
|
|
30
42
|
|
|
31
43
|
export class TransformersProvider implements EmbeddingProvider {
|
|
32
44
|
readonly name = 'transformers-minilm';
|
|
@@ -68,7 +80,7 @@ export class TransformersProvider implements EmbeddingProvider {
|
|
|
68
80
|
|
|
69
81
|
async embed(text: string): Promise<number[]> {
|
|
70
82
|
// Check cache first
|
|
71
|
-
const cached = cache.get(text);
|
|
83
|
+
const cached = cache.get(cacheKey(text));
|
|
72
84
|
if (cached) return cached;
|
|
73
85
|
|
|
74
86
|
const output = await this.extractor(text, {
|
|
@@ -82,7 +94,7 @@ export class TransformersProvider implements EmbeddingProvider {
|
|
|
82
94
|
throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
|
|
83
95
|
}
|
|
84
96
|
|
|
85
|
-
this.cacheSet(text, result);
|
|
97
|
+
this.cacheSet(cacheKey(text), result);
|
|
86
98
|
return result;
|
|
87
99
|
}
|
|
88
100
|
|
|
@@ -93,7 +105,7 @@ export class TransformersProvider implements EmbeddingProvider {
|
|
|
93
105
|
|
|
94
106
|
// Check cache for each text
|
|
95
107
|
for (let i = 0; i < texts.length; i++) {
|
|
96
|
-
const cached = cache.get(texts[i]);
|
|
108
|
+
const cached = cache.get(cacheKey(texts[i]));
|
|
97
109
|
if (cached) {
|
|
98
110
|
results[i] = cached;
|
|
99
111
|
} else {
|
|
@@ -114,7 +126,7 @@ export class TransformersProvider implements EmbeddingProvider {
|
|
|
114
126
|
const vec = Array.from(allVecs[i]) as number[];
|
|
115
127
|
const originalIdx = uncachedIndices[i];
|
|
116
128
|
results[originalIdx] = vec;
|
|
117
|
-
this.cacheSet(uncachedTexts[i], vec);
|
|
129
|
+
this.cacheSet(cacheKey(uncachedTexts[i]), vec);
|
|
118
130
|
}
|
|
119
131
|
}
|
|
120
132
|
|
|
@@ -122,10 +134,20 @@ export class TransformersProvider implements EmbeddingProvider {
|
|
|
122
134
|
}
|
|
123
135
|
|
|
124
136
|
private cacheSet(key: string, value: number[]): void {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
137
|
+
const incomingBytes = value.length * 8;
|
|
138
|
+
if (incomingBytes > maxCachePayloadBytes()) return;
|
|
139
|
+
const existing = cache.get(key);
|
|
140
|
+
if (existing) cachePayloadBytes -= existing.length * 8;
|
|
141
|
+
else {
|
|
142
|
+
while (cache.size >= MAX_CACHE_SIZE || cachePayloadBytes + incomingBytes > maxCachePayloadBytes()) {
|
|
143
|
+
const firstKey = cache.keys().next().value;
|
|
144
|
+
if (firstKey === undefined) break;
|
|
145
|
+
const old = cache.get(firstKey);
|
|
146
|
+
cache.delete(firstKey);
|
|
147
|
+
if (old) cachePayloadBytes -= old.length * 8;
|
|
148
|
+
}
|
|
128
149
|
}
|
|
129
150
|
cache.set(key, value);
|
|
151
|
+
cachePayloadBytes += incomingBytes;
|
|
130
152
|
}
|
|
131
153
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/** Deterministic, model-free quality gate for cross-session memory delivery. */
|
|
2
|
+
|
|
3
|
+
export interface MemoryQualitySample {
|
|
4
|
+
id: string;
|
|
5
|
+
expectedIds: string[];
|
|
6
|
+
shownIds: string[];
|
|
7
|
+
leakedProjectRows?: number;
|
|
8
|
+
staleRowsShown?: number;
|
|
9
|
+
shouldAbstain?: boolean;
|
|
10
|
+
tokens: number;
|
|
11
|
+
tokenBudget: number;
|
|
12
|
+
latencyMs: number;
|
|
13
|
+
rssBytes?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface MemoryQualityGateScore {
|
|
17
|
+
cases: number;
|
|
18
|
+
recallAtK: number;
|
|
19
|
+
precisionAtK: number;
|
|
20
|
+
scopeIsolation: number;
|
|
21
|
+
staleRejection: number;
|
|
22
|
+
abstentionAccuracy: number;
|
|
23
|
+
overBudgetCases: number;
|
|
24
|
+
latencyP50Ms: number;
|
|
25
|
+
latencyP95Ms: number;
|
|
26
|
+
peakRssBytes: number;
|
|
27
|
+
passed: boolean;
|
|
28
|
+
failures: string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function rate(numerator: number, denominator: number): number {
|
|
32
|
+
return denominator === 0 ? 1 : numerator / denominator;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function percentile(values: number[], p: number): number {
|
|
36
|
+
if (values.length === 0) return 0;
|
|
37
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
38
|
+
const index = Math.min(sorted.length - 1, Math.ceil((p / 100) * sorted.length) - 1);
|
|
39
|
+
return sorted[Math.max(0, index)];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function scoreMemoryQualityGate(samples: MemoryQualitySample[]): MemoryQualityGateScore {
|
|
43
|
+
let recalls = 0;
|
|
44
|
+
let precisionNumerator = 0;
|
|
45
|
+
let precisionDenominator = 0;
|
|
46
|
+
let isolated = 0;
|
|
47
|
+
let staleRejected = 0;
|
|
48
|
+
let abstentionCorrect = 0;
|
|
49
|
+
let overBudgetCases = 0;
|
|
50
|
+
const latencies = samples.map(sample => sample.latencyMs);
|
|
51
|
+
const failures: string[] = [];
|
|
52
|
+
|
|
53
|
+
for (const sample of samples) {
|
|
54
|
+
const expected = new Set(sample.expectedIds);
|
|
55
|
+
const shown = new Set(sample.shownIds);
|
|
56
|
+
if ([...expected].every(id => shown.has(id))) recalls++;
|
|
57
|
+
precisionNumerator += [...shown].filter(id => expected.has(id)).length;
|
|
58
|
+
precisionDenominator += shown.size;
|
|
59
|
+
if (!sample.leakedProjectRows) isolated++;
|
|
60
|
+
if (!sample.staleRowsShown) staleRejected++;
|
|
61
|
+
const abstained = shown.size === 0;
|
|
62
|
+
if (Boolean(sample.shouldAbstain) === abstained) abstentionCorrect++;
|
|
63
|
+
if (sample.tokens > sample.tokenBudget) overBudgetCases++;
|
|
64
|
+
if (sample.shouldAbstain && !abstained) failures.push(`${sample.id}: failed to abstain`);
|
|
65
|
+
if (!sample.shouldAbstain && ![...expected].every(id => shown.has(id))) failures.push(`${sample.id}: expected memory missing`);
|
|
66
|
+
if (sample.leakedProjectRows) failures.push(`${sample.id}: cross-project leakage`);
|
|
67
|
+
if (sample.staleRowsShown) failures.push(`${sample.id}: stale memory surfaced`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const score: MemoryQualityGateScore = {
|
|
71
|
+
cases: samples.length,
|
|
72
|
+
recallAtK: rate(recalls, samples.length),
|
|
73
|
+
precisionAtK: rate(precisionNumerator, precisionDenominator),
|
|
74
|
+
scopeIsolation: rate(isolated, samples.length),
|
|
75
|
+
staleRejection: rate(staleRejected, samples.length),
|
|
76
|
+
abstentionAccuracy: rate(abstentionCorrect, samples.length),
|
|
77
|
+
overBudgetCases,
|
|
78
|
+
latencyP50Ms: percentile(latencies, 50),
|
|
79
|
+
latencyP95Ms: percentile(latencies, 95),
|
|
80
|
+
peakRssBytes: Math.max(0, ...samples.map(sample => sample.rssBytes ?? 0)),
|
|
81
|
+
passed: false,
|
|
82
|
+
failures,
|
|
83
|
+
};
|
|
84
|
+
score.passed = score.recallAtK === 1
|
|
85
|
+
&& score.scopeIsolation === 1
|
|
86
|
+
&& score.staleRejection === 1
|
|
87
|
+
&& score.abstentionAccuracy === 1
|
|
88
|
+
&& score.overBudgetCases === 0;
|
|
89
|
+
return score;
|
|
90
|
+
}
|
|
@@ -40,6 +40,7 @@ export const OFFICIAL_MEMORIX_SKILLS: OfficialMemorixSkill[] = [
|
|
|
40
40
|
'| Need the sequence around one memory | `memorix_timeline` | `memorix memory timeline --id <id>` |',
|
|
41
41
|
'| Check source and freshness for a memory | `memorix_evidence` | `memorix evidence list|get|events` |',
|
|
42
42
|
'| Record whether a memory helped, conflicted, or was corrected | `memorix_feedback` | `memorix feedback record|show|audit` |',
|
|
43
|
+
'| Track task requirements, decisions, verification, and outcome | `memorix_continuity` | `memorix continuity start|record|show|close` |',
|
|
43
44
|
'| Inspect or import controlled media | `memorix_media` | `memorix media import|attach|list|show|status` |',
|
|
44
45
|
'| Learned reusable project knowledge | `memorix_store` | `memorix memory store --type <type> --entity <name> --title "<title>" "<text>"` |',
|
|
45
46
|
'| Task or bug is complete/outdated | `memorix_resolve` | `memorix memory resolve --ids <ids>` |',
|
|
@@ -63,6 +64,7 @@ export const OFFICIAL_MEMORIX_SKILLS: OfficialMemorixSkill[] = [
|
|
|
63
64
|
'3. Use stale or unbound memory only as a warning or search lead.',
|
|
64
65
|
'4. Store durable decisions, fixes, and gotchas after the project state changes.',
|
|
65
66
|
'5. Resolve completed or obsolete memories so future briefs stay small.',
|
|
67
|
+
'6. For multi-step work, start a continuity ledger and pass its taskId back to memorix_project_context.',
|
|
66
68
|
'',
|
|
67
69
|
'## Store Rules',
|
|
68
70
|
'',
|
|
@@ -72,7 +72,7 @@ export const MEMORIX_INTEGRATIONS: MemorixIntegration[] = [
|
|
|
72
72
|
status: 'ready',
|
|
73
73
|
install: 'memorix setup --agent grok --global',
|
|
74
74
|
surfaces: ['hooks', 'AGENTS.md'],
|
|
75
|
-
note: 'Writes ~/.grok/hooks/memorix.json and leaves Grok MCP host-owned
|
|
75
|
+
note: 'Writes ~/.grok/hooks/memorix.json and leaves Grok MCP host-owned by default. Pass --mcp http to write [mcp_servers.memorix] in Grok config.toml.',
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
78
|
agent: 'cursor',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CodeGraphProviderQuality } from '../codegraph/types.js';
|
|
2
2
|
import type { TaskWorkset } from './workset.js';
|
|
3
|
+
import type { TaskContinuityLedger } from './task-continuity.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* A small machine-readable counterpart to the agent-facing Workset prompt.
|
|
@@ -18,6 +19,11 @@ export interface BoundedContextReceipt {
|
|
|
18
19
|
snapshotId?: string;
|
|
19
20
|
};
|
|
20
21
|
loadout?: TaskWorkset['agentLoadout'];
|
|
22
|
+
continuity?: {
|
|
23
|
+
taskId: string;
|
|
24
|
+
status: TaskContinuityLedger['status'];
|
|
25
|
+
outcome: TaskContinuityLedger['outcome'];
|
|
26
|
+
};
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
export function buildBoundedContextReceipt(input: {
|
|
@@ -37,5 +43,14 @@ export function buildBoundedContextReceipt(input: {
|
|
|
37
43
|
...(input.workset.provenance.snapshotId ? { snapshotId: input.workset.provenance.snapshotId } : {}),
|
|
38
44
|
},
|
|
39
45
|
...(input.workset.agentLoadout ? { loadout: input.workset.agentLoadout } : {}),
|
|
46
|
+
...(input.workset.continuity
|
|
47
|
+
? {
|
|
48
|
+
continuity: {
|
|
49
|
+
taskId: input.workset.continuity.taskId,
|
|
50
|
+
status: input.workset.continuity.status,
|
|
51
|
+
outcome: input.workset.continuity.outcome,
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
: {}),
|
|
40
55
|
};
|
|
41
56
|
}
|