memorix 1.2.2 → 1.2.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/CHANGELOG.md +7 -0
- package/TEAM.md +86 -86
- package/dist/cli/index.js +34 -18
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +17 -8
- package/dist/index.js.map +1 -1
- package/dist/maintenance-runner.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +7 -0
- package/dist/sdk.js +17 -8
- package/dist/sdk.js.map +1 -1
- package/docs/DESIGN_DECISIONS.md +357 -357
- package/docs/dev-log/progress.txt +18 -8
- package/package.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/src/audit/index.ts +156 -156
- package/src/cli/commands/audit-list.ts +89 -89
- package/src/cli/commands/background.ts +659 -659
- package/src/cli/commands/formation.ts +48 -48
- package/src/cli/commands/git-hook-install.ts +111 -111
- package/src/cli/commands/handoff.ts +54 -54
- package/src/cli/commands/hooks-status.ts +63 -63
- package/src/cli/commands/ingest-commit.ts +153 -153
- package/src/cli/commands/ingest-image.ts +66 -66
- package/src/cli/commands/ingest-log.ts +180 -180
- package/src/cli/commands/ingest.ts +44 -44
- package/src/cli/commands/integrate-shared.ts +15 -15
- package/src/cli/commands/lock.ts +82 -82
- package/src/cli/commands/message.ts +104 -104
- package/src/cli/commands/poll.ts +58 -58
- package/src/cli/commands/purge-all-memory.ts +85 -85
- package/src/cli/commands/purge-project-memory.ts +83 -83
- package/src/cli/commands/reasoning.ts +118 -118
- package/src/cli/commands/serve-shared.ts +118 -118
- package/src/cli/commands/session.ts +15 -7
- package/src/cli/commands/skills.ts +114 -114
- package/src/cli/commands/task.ts +167 -167
- package/src/cli/commands/transfer.ts +47 -47
- package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
- package/src/cli/tui/ChatView.tsx +234 -234
- package/src/cli/tui/CommandBar.tsx +312 -312
- package/src/cli/tui/ContextRail.tsx +118 -118
- package/src/cli/tui/HeaderBar.tsx +72 -72
- package/src/cli/tui/LogoBanner.tsx +51 -51
- package/src/cli/tui/Sidebar.tsx +179 -179
- package/src/cli/tui/index.ts +41 -41
- package/src/cli/tui/markdown-render.tsx +371 -371
- package/src/cli/tui/session-service.ts +3 -2
- package/src/cli/tui/use-mouse.ts +157 -157
- package/src/cli/tui/useNavigation.ts +56 -56
- package/src/cli/update-checker.ts +211 -211
- package/src/cli/version.ts +7 -7
- package/src/cli/workbench.ts +1 -1
- package/src/compact/token-budget.ts +74 -74
- package/src/dashboard/project-classification.ts +64 -64
- package/src/embedding/fastembed-provider.ts +142 -142
- package/src/embedding/transformers-provider.ts +111 -111
- package/src/git/extractor.ts +209 -209
- package/src/git/hooks-path.ts +85 -85
- package/src/hooks/pattern-detector.ts +173 -173
- package/src/hooks/significance-filter.ts +250 -250
- package/src/llm/memory-manager.ts +328 -328
- package/src/llm/provider.ts +885 -885
- package/src/llm/quality.ts +248 -248
- package/src/memory/attribution-guard.ts +249 -249
- package/src/memory/disclosure-policy.ts +135 -135
- package/src/memory/entity-extractor.ts +197 -197
- package/src/memory/formation/evaluate.ts +217 -217
- package/src/memory/formation/extract.ts +361 -361
- package/src/memory/formation/index.ts +417 -417
- package/src/memory/formation/resolve.ts +344 -344
- package/src/memory/formation/types.ts +315 -315
- package/src/memory/freshness.ts +122 -122
- package/src/memory/graph.ts +197 -197
- package/src/memory/refs.ts +94 -94
- package/src/memory/secret-filter.ts +79 -79
- package/src/memory/session.ts +24 -9
- package/src/multimodal/image-loader.ts +143 -143
- package/src/orchestrate/adapters/claude-stream.ts +192 -192
- package/src/orchestrate/adapters/claude.ts +111 -111
- package/src/orchestrate/adapters/codex-stream.ts +134 -134
- package/src/orchestrate/adapters/codex.ts +41 -41
- package/src/orchestrate/adapters/gemini-stream.ts +166 -166
- package/src/orchestrate/adapters/gemini.ts +42 -42
- package/src/orchestrate/adapters/index.ts +73 -73
- package/src/orchestrate/adapters/opencode-stream.ts +143 -143
- package/src/orchestrate/adapters/opencode.ts +47 -47
- package/src/orchestrate/adapters/spawn-helper.ts +286 -286
- package/src/orchestrate/adapters/types.ts +77 -77
- package/src/orchestrate/capability-router.ts +284 -284
- package/src/orchestrate/context-compact.ts +188 -188
- package/src/orchestrate/cost-tracker.ts +219 -219
- package/src/orchestrate/error-recovery.ts +191 -191
- package/src/orchestrate/evidence.ts +140 -140
- package/src/orchestrate/ledger.ts +110 -110
- package/src/orchestrate/memorix-bridge.ts +343 -343
- package/src/orchestrate/output-budget.ts +80 -80
- package/src/orchestrate/permission.ts +152 -152
- package/src/orchestrate/pipeline-trace.ts +131 -131
- package/src/orchestrate/prompt-builder.ts +155 -155
- package/src/orchestrate/ring-buffer.ts +37 -37
- package/src/orchestrate/task-graph.ts +389 -389
- package/src/orchestrate/worktree.ts +232 -232
- package/src/project/aliases.ts +374 -374
- package/src/project/detector.ts +268 -268
- package/src/rules/adapters/claude-code.ts +99 -99
- package/src/rules/adapters/codex.ts +97 -97
- package/src/rules/adapters/copilot.ts +124 -124
- package/src/rules/adapters/cursor.ts +114 -114
- package/src/rules/adapters/kiro.ts +126 -126
- package/src/rules/adapters/trae.ts +56 -56
- package/src/rules/adapters/windsurf.ts +83 -83
- package/src/rules/syncer.ts +235 -235
- package/src/sdk.ts +299 -299
- package/src/search/intent-detector.ts +289 -289
- package/src/search/query-expansion.ts +52 -52
- package/src/server/formation-timeout.ts +27 -27
- package/src/server.ts +7 -2
- package/src/skills/mini-skills.ts +386 -386
- package/src/store/chat-store.ts +119 -119
- package/src/store/graph-store.ts +249 -249
- package/src/store/mini-skill-store.ts +349 -349
- package/src/store/persistence-json.ts +212 -212
- package/src/store/persistence.ts +291 -291
- package/src/store/project-affinity.ts +195 -195
- package/src/team/event-bus.ts +76 -76
- package/src/team/file-locks.ts +173 -173
- package/src/team/handoff.ts +161 -161
- package/src/team/messages.ts +203 -203
- package/src/team/poll.ts +132 -132
- package/src/team/tasks.ts +211 -211
- package/src/workspace/mcp-adapters/codex.ts +191 -191
- package/src/workspace/mcp-adapters/copilot.ts +105 -105
- package/src/workspace/mcp-adapters/cursor.ts +53 -53
- package/src/workspace/mcp-adapters/kiro.ts +64 -64
- package/src/workspace/mcp-adapters/opencode.ts +123 -123
- package/src/workspace/mcp-adapters/trae.ts +134 -134
- package/src/workspace/mcp-adapters/windsurf.ts +91 -91
- package/src/workspace/sanitizer.ts +60 -60
- package/src/workspace/workflow-sync.ts +131 -131
package/src/team/file-locks.ts
CHANGED
|
@@ -1,173 +1,173 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* File Lock Registry — Advisory file locks for multi-agent coordination
|
|
3
|
-
*
|
|
4
|
-
* Prevents conflicting edits when multiple agents work on the same project.
|
|
5
|
-
* Locks are advisory (not enforced at OS level) with TTL auto-release.
|
|
6
|
-
* Default TTL: 10 minutes. Agents see lock status via session_start injection.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
// ─── Types ───────────────────────────────────────────────────────────
|
|
10
|
-
|
|
11
|
-
export interface LockResult {
|
|
12
|
-
success: boolean;
|
|
13
|
-
lockedBy: string;
|
|
14
|
-
file: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface LockInfo {
|
|
18
|
-
file: string;
|
|
19
|
-
lockedBy: string;
|
|
20
|
-
lockedAt: Date;
|
|
21
|
-
expiresAt: Date;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface LockOptions {
|
|
25
|
-
ttlMs?: number;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const DEFAULT_TTL_MS = 10 * 60 * 1000; // 10 minutes
|
|
29
|
-
|
|
30
|
-
// ─── Internal Lock Entry ─────────────────────────────────────────────
|
|
31
|
-
|
|
32
|
-
interface LockEntry {
|
|
33
|
-
file: string;
|
|
34
|
-
lockedBy: string;
|
|
35
|
-
lockedAt: Date;
|
|
36
|
-
expiresAt: Date;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// ─── Registry ────────────────────────────────────────────────────────
|
|
40
|
-
|
|
41
|
-
export class FileLockRegistry {
|
|
42
|
-
private locks = new Map<string, LockEntry>();
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Attempt to lock a file for an agent.
|
|
46
|
-
* Returns success:true if lock acquired, or success:false with current owner.
|
|
47
|
-
* Same agent re-locking is idempotent (refreshes TTL).
|
|
48
|
-
*/
|
|
49
|
-
lock(file: string, agentId: string, options?: LockOptions): LockResult {
|
|
50
|
-
const ttl = options?.ttlMs ?? DEFAULT_TTL_MS;
|
|
51
|
-
|
|
52
|
-
// Clean expired locks first
|
|
53
|
-
this.cleanExpiredEntry(file);
|
|
54
|
-
|
|
55
|
-
const existing = this.locks.get(file);
|
|
56
|
-
|
|
57
|
-
if (existing) {
|
|
58
|
-
if (existing.lockedBy === agentId) {
|
|
59
|
-
// Same agent — refresh TTL
|
|
60
|
-
existing.expiresAt = new Date(Date.now() + ttl);
|
|
61
|
-
return { success: true, lockedBy: agentId, file };
|
|
62
|
-
}
|
|
63
|
-
// Different agent holds the lock
|
|
64
|
-
return { success: false, lockedBy: existing.lockedBy, file };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// No lock — acquire
|
|
68
|
-
const now = new Date();
|
|
69
|
-
this.locks.set(file, {
|
|
70
|
-
file,
|
|
71
|
-
lockedBy: agentId,
|
|
72
|
-
lockedAt: now,
|
|
73
|
-
expiresAt: new Date(now.getTime() + ttl),
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
return { success: true, lockedBy: agentId, file };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Release a lock. Only the owner can release.
|
|
81
|
-
* Returns true if released, false if not found or not owner.
|
|
82
|
-
*/
|
|
83
|
-
unlock(file: string, agentId: string): boolean {
|
|
84
|
-
const entry = this.locks.get(file);
|
|
85
|
-
if (!entry) return false;
|
|
86
|
-
if (entry.lockedBy !== agentId) return false;
|
|
87
|
-
|
|
88
|
-
this.locks.delete(file);
|
|
89
|
-
return true;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Get lock status for a specific file. Returns null if unlocked.
|
|
94
|
-
*/
|
|
95
|
-
getStatus(file: string): LockInfo | null {
|
|
96
|
-
this.cleanExpiredEntry(file);
|
|
97
|
-
const entry = this.locks.get(file);
|
|
98
|
-
if (!entry) return null;
|
|
99
|
-
return { ...entry };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* List all active locks, optionally filtered by agent.
|
|
104
|
-
*/
|
|
105
|
-
listLocks(agentId?: string): LockInfo[] {
|
|
106
|
-
this.cleanExpired();
|
|
107
|
-
const all = [...this.locks.values()];
|
|
108
|
-
const filtered = agentId ? all.filter(l => l.lockedBy === agentId) : all;
|
|
109
|
-
return filtered.map(l => ({ ...l }));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Release all locks held by a specific agent. Returns count released.
|
|
114
|
-
*/
|
|
115
|
-
releaseAll(agentId: string): number {
|
|
116
|
-
let count = 0;
|
|
117
|
-
for (const [file, entry] of this.locks) {
|
|
118
|
-
if (entry.lockedBy === agentId) {
|
|
119
|
-
this.locks.delete(file);
|
|
120
|
-
count++;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return count;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Remove all expired locks.
|
|
128
|
-
*/
|
|
129
|
-
cleanExpired(): void {
|
|
130
|
-
const now = Date.now();
|
|
131
|
-
for (const [file, entry] of this.locks) {
|
|
132
|
-
if (entry.expiresAt.getTime() <= now) {
|
|
133
|
-
this.locks.delete(file);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Check and remove a single expired lock entry.
|
|
140
|
-
*/
|
|
141
|
-
private cleanExpiredEntry(file: string): void {
|
|
142
|
-
const entry = this.locks.get(file);
|
|
143
|
-
if (entry && entry.expiresAt.getTime() <= Date.now()) {
|
|
144
|
-
this.locks.delete(file);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/** Serialize state for file persistence */
|
|
149
|
-
serialize(): { locks: Record<string, unknown> } {
|
|
150
|
-
const locks: Record<string, unknown> = {};
|
|
151
|
-
for (const [file, entry] of this.locks) {
|
|
152
|
-
locks[file] = {
|
|
153
|
-
...entry,
|
|
154
|
-
lockedAt: entry.lockedAt.toISOString(),
|
|
155
|
-
expiresAt: entry.expiresAt.toISOString(),
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
return { locks };
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/** Hydrate state from file persistence */
|
|
162
|
-
hydrate(data: { locks?: Record<string, any> }): void {
|
|
163
|
-
this.locks.clear();
|
|
164
|
-
if (!data?.locks) return;
|
|
165
|
-
for (const [file, raw] of Object.entries(data.locks)) {
|
|
166
|
-
this.locks.set(file, {
|
|
167
|
-
...(raw as any),
|
|
168
|
-
lockedAt: new Date((raw as any).lockedAt),
|
|
169
|
-
expiresAt: new Date((raw as any).expiresAt),
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* File Lock Registry — Advisory file locks for multi-agent coordination
|
|
3
|
+
*
|
|
4
|
+
* Prevents conflicting edits when multiple agents work on the same project.
|
|
5
|
+
* Locks are advisory (not enforced at OS level) with TTL auto-release.
|
|
6
|
+
* Default TTL: 10 minutes. Agents see lock status via session_start injection.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ─── Types ───────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
export interface LockResult {
|
|
12
|
+
success: boolean;
|
|
13
|
+
lockedBy: string;
|
|
14
|
+
file: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface LockInfo {
|
|
18
|
+
file: string;
|
|
19
|
+
lockedBy: string;
|
|
20
|
+
lockedAt: Date;
|
|
21
|
+
expiresAt: Date;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface LockOptions {
|
|
25
|
+
ttlMs?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const DEFAULT_TTL_MS = 10 * 60 * 1000; // 10 minutes
|
|
29
|
+
|
|
30
|
+
// ─── Internal Lock Entry ─────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
interface LockEntry {
|
|
33
|
+
file: string;
|
|
34
|
+
lockedBy: string;
|
|
35
|
+
lockedAt: Date;
|
|
36
|
+
expiresAt: Date;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ─── Registry ────────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
export class FileLockRegistry {
|
|
42
|
+
private locks = new Map<string, LockEntry>();
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Attempt to lock a file for an agent.
|
|
46
|
+
* Returns success:true if lock acquired, or success:false with current owner.
|
|
47
|
+
* Same agent re-locking is idempotent (refreshes TTL).
|
|
48
|
+
*/
|
|
49
|
+
lock(file: string, agentId: string, options?: LockOptions): LockResult {
|
|
50
|
+
const ttl = options?.ttlMs ?? DEFAULT_TTL_MS;
|
|
51
|
+
|
|
52
|
+
// Clean expired locks first
|
|
53
|
+
this.cleanExpiredEntry(file);
|
|
54
|
+
|
|
55
|
+
const existing = this.locks.get(file);
|
|
56
|
+
|
|
57
|
+
if (existing) {
|
|
58
|
+
if (existing.lockedBy === agentId) {
|
|
59
|
+
// Same agent — refresh TTL
|
|
60
|
+
existing.expiresAt = new Date(Date.now() + ttl);
|
|
61
|
+
return { success: true, lockedBy: agentId, file };
|
|
62
|
+
}
|
|
63
|
+
// Different agent holds the lock
|
|
64
|
+
return { success: false, lockedBy: existing.lockedBy, file };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// No lock — acquire
|
|
68
|
+
const now = new Date();
|
|
69
|
+
this.locks.set(file, {
|
|
70
|
+
file,
|
|
71
|
+
lockedBy: agentId,
|
|
72
|
+
lockedAt: now,
|
|
73
|
+
expiresAt: new Date(now.getTime() + ttl),
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return { success: true, lockedBy: agentId, file };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Release a lock. Only the owner can release.
|
|
81
|
+
* Returns true if released, false if not found or not owner.
|
|
82
|
+
*/
|
|
83
|
+
unlock(file: string, agentId: string): boolean {
|
|
84
|
+
const entry = this.locks.get(file);
|
|
85
|
+
if (!entry) return false;
|
|
86
|
+
if (entry.lockedBy !== agentId) return false;
|
|
87
|
+
|
|
88
|
+
this.locks.delete(file);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get lock status for a specific file. Returns null if unlocked.
|
|
94
|
+
*/
|
|
95
|
+
getStatus(file: string): LockInfo | null {
|
|
96
|
+
this.cleanExpiredEntry(file);
|
|
97
|
+
const entry = this.locks.get(file);
|
|
98
|
+
if (!entry) return null;
|
|
99
|
+
return { ...entry };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* List all active locks, optionally filtered by agent.
|
|
104
|
+
*/
|
|
105
|
+
listLocks(agentId?: string): LockInfo[] {
|
|
106
|
+
this.cleanExpired();
|
|
107
|
+
const all = [...this.locks.values()];
|
|
108
|
+
const filtered = agentId ? all.filter(l => l.lockedBy === agentId) : all;
|
|
109
|
+
return filtered.map(l => ({ ...l }));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Release all locks held by a specific agent. Returns count released.
|
|
114
|
+
*/
|
|
115
|
+
releaseAll(agentId: string): number {
|
|
116
|
+
let count = 0;
|
|
117
|
+
for (const [file, entry] of this.locks) {
|
|
118
|
+
if (entry.lockedBy === agentId) {
|
|
119
|
+
this.locks.delete(file);
|
|
120
|
+
count++;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return count;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Remove all expired locks.
|
|
128
|
+
*/
|
|
129
|
+
cleanExpired(): void {
|
|
130
|
+
const now = Date.now();
|
|
131
|
+
for (const [file, entry] of this.locks) {
|
|
132
|
+
if (entry.expiresAt.getTime() <= now) {
|
|
133
|
+
this.locks.delete(file);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Check and remove a single expired lock entry.
|
|
140
|
+
*/
|
|
141
|
+
private cleanExpiredEntry(file: string): void {
|
|
142
|
+
const entry = this.locks.get(file);
|
|
143
|
+
if (entry && entry.expiresAt.getTime() <= Date.now()) {
|
|
144
|
+
this.locks.delete(file);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Serialize state for file persistence */
|
|
149
|
+
serialize(): { locks: Record<string, unknown> } {
|
|
150
|
+
const locks: Record<string, unknown> = {};
|
|
151
|
+
for (const [file, entry] of this.locks) {
|
|
152
|
+
locks[file] = {
|
|
153
|
+
...entry,
|
|
154
|
+
lockedAt: entry.lockedAt.toISOString(),
|
|
155
|
+
expiresAt: entry.expiresAt.toISOString(),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return { locks };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Hydrate state from file persistence */
|
|
162
|
+
hydrate(data: { locks?: Record<string, any> }): void {
|
|
163
|
+
this.locks.clear();
|
|
164
|
+
if (!data?.locks) return;
|
|
165
|
+
for (const [file, raw] of Object.entries(data.locks)) {
|
|
166
|
+
this.locks.set(file, {
|
|
167
|
+
...(raw as any),
|
|
168
|
+
lockedAt: new Date((raw as any).lockedAt),
|
|
169
|
+
expiresAt: new Date((raw as any).expiresAt),
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|