memorix 1.1.13 → 1.2.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.
Files changed (97) hide show
  1. package/CHANGELOG.md +33 -1
  2. package/README.md +6 -3
  3. package/README.zh-CN.md +6 -3
  4. package/dist/cli/index.js +36639 -31279
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/dashboard/static/app.js +50 -30
  7. package/dist/index.js +6636 -1778
  8. package/dist/index.js.map +1 -1
  9. package/dist/maintenance-runner.d.ts +1 -1
  10. package/dist/maintenance-runner.js +3775 -302
  11. package/dist/maintenance-runner.js.map +1 -1
  12. package/dist/memcode-runtime/CHANGELOG.md +33 -1
  13. package/dist/sdk.d.ts +1 -1
  14. package/dist/sdk.js +6634 -1776
  15. package/dist/sdk.js.map +1 -1
  16. package/docs/1.2.0-CLAIM-LEDGER.md +72 -0
  17. package/docs/1.2.0-CODE-STATE.md +61 -0
  18. package/docs/1.2.0-DEVELOPMENT-CHARTER.md +255 -0
  19. package/docs/1.2.0-DYNAMIC-LIFECYCLE.md +71 -0
  20. package/docs/1.2.0-EVALUATION-HARNESS.md +51 -0
  21. package/docs/1.2.0-IMPLEMENTATION-PLAN.md +554 -0
  22. package/docs/1.2.0-KNOWLEDGE-WORKFLOW-RESEARCH.md +205 -0
  23. package/docs/1.2.0-KNOWLEDGE-WORKSPACE.md +68 -0
  24. package/docs/1.2.0-PRODUCT-STORY.md +234 -0
  25. package/docs/1.2.0-PROVIDER-QUALITY.md +189 -0
  26. package/docs/1.2.0-WORKFLOW-INHERITANCE.md +101 -0
  27. package/docs/1.2.0-WORKSET-RETRIEVAL.md +80 -0
  28. package/docs/AGENT_OPERATOR_PLAYBOOK.md +6 -3
  29. package/docs/API_REFERENCE.md +27 -6
  30. package/docs/CONFIGURATION.md +21 -3
  31. package/docs/DEVELOPMENT.md +4 -0
  32. package/docs/README.md +17 -2
  33. package/docs/SETUP.md +7 -1
  34. package/docs/dev-log/progress.txt +120 -40
  35. package/docs/knowledge/workflows/memorix-release.md +57 -0
  36. package/llms-full.txt +16 -2
  37. package/llms.txt +9 -4
  38. package/package.json +3 -2
  39. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  40. package/src/cli/capability-map.ts +1 -0
  41. package/src/cli/commands/codegraph.ts +111 -9
  42. package/src/cli/commands/context.ts +2 -0
  43. package/src/cli/commands/doctor.ts +73 -4
  44. package/src/cli/commands/knowledge.ts +322 -0
  45. package/src/cli/commands/serve-http.ts +26 -42
  46. package/src/cli/commands/setup.ts +9 -3
  47. package/src/cli/index.ts +3 -1
  48. package/src/cli/tui/App.tsx +1 -1
  49. package/src/cli/tui/Panels.tsx +8 -8
  50. package/src/cli/tui/theme.ts +1 -1
  51. package/src/cli/tui/views/GraphView.tsx +8 -7
  52. package/src/cli/tui/views/KnowledgeView.tsx +9 -9
  53. package/src/codegraph/auto-context.ts +169 -19
  54. package/src/codegraph/code-state.ts +95 -0
  55. package/src/codegraph/context-pack.ts +82 -1
  56. package/src/codegraph/current-facts.ts +19 -1
  57. package/src/codegraph/external-provider.ts +581 -0
  58. package/src/codegraph/lite-provider.ts +64 -19
  59. package/src/codegraph/project-context.ts +9 -1
  60. package/src/codegraph/store.ts +154 -6
  61. package/src/codegraph/task-lens.ts +49 -5
  62. package/src/codegraph/types.ts +117 -0
  63. package/src/config/resolved-config.ts +28 -0
  64. package/src/config/toml-loader.ts +3 -0
  65. package/src/config/yaml-loader.ts +6 -0
  66. package/src/dashboard/server.ts +30 -47
  67. package/src/evaluation/workset-evaluation.ts +120 -0
  68. package/src/hooks/handler.ts +48 -6
  69. package/src/knowledge/claim-store.ts +267 -0
  70. package/src/knowledge/claims.ts +587 -0
  71. package/src/knowledge/markdown.ts +129 -0
  72. package/src/knowledge/types.ts +158 -0
  73. package/src/knowledge/wiki.ts +524 -0
  74. package/src/knowledge/workflow-store.ts +168 -0
  75. package/src/knowledge/workflow-types.ts +95 -0
  76. package/src/knowledge/workflows.ts +774 -0
  77. package/src/knowledge/workset.ts +515 -0
  78. package/src/knowledge/workspace-store.ts +220 -0
  79. package/src/knowledge/workspace-types.ts +106 -0
  80. package/src/knowledge/workspace.ts +220 -0
  81. package/src/memory/auto-relations.ts +21 -0
  82. package/src/memory/graph-scope.ts +46 -0
  83. package/src/memory/observations.ts +19 -0
  84. package/src/orchestrate/verify-gate.ts +33 -10
  85. package/src/runtime/control-plane-maintenance.ts +5 -0
  86. package/src/runtime/isolated-maintenance.ts +5 -0
  87. package/src/runtime/lifecycle-status.ts +102 -0
  88. package/src/runtime/lifecycle.ts +107 -0
  89. package/src/runtime/maintenance-jobs.ts +5 -0
  90. package/src/runtime/project-maintenance.ts +190 -0
  91. package/src/server/tool-profile.ts +3 -2
  92. package/src/server.ts +424 -22
  93. package/src/store/file-lock.ts +24 -4
  94. package/src/store/sqlite-db.ts +307 -0
  95. package/src/wiki/generator.ts +4 -2
  96. package/src/wiki/knowledge-graph.ts +7 -4
  97. package/src/wiki/types.ts +16 -4
@@ -0,0 +1,267 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { sanitizeCredentials } from '../memory/secret-filter.js';
3
+ import { getDatabase } from '../store/sqlite-db.js';
4
+ import type {
5
+ ClaimConflict,
6
+ ClaimEvent,
7
+ ClaimEvidenceRef,
8
+ KnowledgeClaim,
9
+ KnowledgeClaimStatus,
10
+ } from './types.js';
11
+
12
+ function optionalText(value: unknown): string | undefined {
13
+ return typeof value === 'string' && value ? value : undefined;
14
+ }
15
+
16
+ function rowToClaim(row: any): KnowledgeClaim {
17
+ return {
18
+ id: row.id,
19
+ projectId: row.projectId,
20
+ subject: row.subject,
21
+ predicate: row.predicate,
22
+ objectValue: row.objectValue,
23
+ scope: row.scope,
24
+ claimKey: row.claimKey,
25
+ conflictKey: row.conflictKey,
26
+ status: row.status,
27
+ confidence: Number(row.confidence),
28
+ observedAt: row.observedAt,
29
+ ...(optionalText(row.validFrom) ? { validFrom: row.validFrom } : {}),
30
+ ...(optionalText(row.validTo) ? { validTo: row.validTo } : {}),
31
+ ...(optionalText(row.supersededBy) ? { supersededBy: row.supersededBy } : {}),
32
+ reviewState: row.reviewState,
33
+ origin: row.origin,
34
+ createdAt: row.createdAt,
35
+ updatedAt: row.updatedAt,
36
+ };
37
+ }
38
+
39
+ function rowToEvidence(row: any): ClaimEvidenceRef {
40
+ return {
41
+ id: row.id,
42
+ claimId: row.claimId,
43
+ evidenceKind: row.evidenceKind,
44
+ evidenceId: row.evidenceId,
45
+ relation: row.relation,
46
+ ...(optionalText(row.snapshotId) ? { snapshotId: row.snapshotId } : {}),
47
+ ...(optionalText(row.locator) ? { locator: row.locator } : {}),
48
+ ...(optionalText(row.capturedHash) ? { capturedHash: row.capturedHash } : {}),
49
+ createdAt: row.createdAt,
50
+ };
51
+ }
52
+
53
+ function rowToEvent(row: any): ClaimEvent {
54
+ return {
55
+ id: row.id,
56
+ projectId: row.projectId,
57
+ claimId: row.claimId,
58
+ kind: row.kind,
59
+ ...(optionalText(row.fromStatus) ? { fromStatus: row.fromStatus } : {}),
60
+ ...(optionalText(row.toStatus) ? { toStatus: row.toStatus } : {}),
61
+ ...(optionalText(row.relatedClaimId) ? { relatedClaimId: row.relatedClaimId } : {}),
62
+ ...(optionalText(row.detail) ? { detail: row.detail } : {}),
63
+ createdAt: row.createdAt,
64
+ };
65
+ }
66
+
67
+ function evidenceKey(input: Omit<ClaimEvidenceRef, 'id' | 'claimId' | 'createdAt'>): string {
68
+ return createHash('sha256').update(JSON.stringify([
69
+ input.evidenceKind,
70
+ input.evidenceId,
71
+ input.relation,
72
+ input.snapshotId ?? '',
73
+ input.locator ?? '',
74
+ input.capturedHash ?? '',
75
+ ])).digest('hex');
76
+ }
77
+
78
+ /**
79
+ * SQLite access for the claim ledger. Domain policy belongs in claims.ts; this
80
+ * class only preserves records and their audit trail atomically.
81
+ */
82
+ export class ClaimStore {
83
+ private db: any = null;
84
+
85
+ async init(dataDir: string): Promise<void> {
86
+ this.db = getDatabase(dataDir);
87
+ }
88
+
89
+ transaction<T>(fn: () => T): T {
90
+ return this.db.transaction(fn)();
91
+ }
92
+
93
+ getClaim(id: string): KnowledgeClaim | undefined {
94
+ const row = this.db.prepare('SELECT * FROM knowledge_claims WHERE id = ?').get(id);
95
+ return row ? rowToClaim(row) : undefined;
96
+ }
97
+
98
+ listClaims(
99
+ projectId: string,
100
+ options: { statuses?: readonly KnowledgeClaimStatus[]; limit?: number } = {},
101
+ ): KnowledgeClaim[] {
102
+ const limit = Math.max(1, Math.min(1_000, Math.floor(options.limit ?? 500)));
103
+ if (options.statuses && options.statuses.length > 0) {
104
+ return this.db.prepare(`
105
+ SELECT * FROM knowledge_claims
106
+ WHERE projectId = ? AND status IN (SELECT value FROM json_each(?))
107
+ ORDER BY updatedAt DESC, id
108
+ LIMIT ?
109
+ `).all(projectId, JSON.stringify([...new Set(options.statuses)]), limit).map(rowToClaim);
110
+ }
111
+ return this.db.prepare(`
112
+ SELECT * FROM knowledge_claims
113
+ WHERE projectId = ?
114
+ ORDER BY updatedAt DESC, id
115
+ LIMIT ?
116
+ `).all(projectId, limit).map(rowToClaim);
117
+ }
118
+
119
+ findReusableClaim(projectId: string, claimKey: string): KnowledgeClaim | undefined {
120
+ const row = this.db.prepare(`
121
+ SELECT * FROM knowledge_claims
122
+ WHERE projectId = ? AND claimKey = ? AND status IN ('active', 'disputed')
123
+ ORDER BY updatedAt DESC
124
+ LIMIT 1
125
+ `).get(projectId, claimKey);
126
+ return row ? rowToClaim(row) : undefined;
127
+ }
128
+
129
+ listClaimsByConflictKey(projectId: string, conflictKey: string): KnowledgeClaim[] {
130
+ return this.db.prepare(`
131
+ SELECT * FROM knowledge_claims
132
+ WHERE projectId = ? AND conflictKey = ?
133
+ ORDER BY createdAt ASC, id
134
+ `).all(projectId, conflictKey).map(rowToClaim);
135
+ }
136
+
137
+ listConflicts(projectId: string): ClaimConflict[] {
138
+ const rows = this.db.prepare(`
139
+ SELECT conflictKey
140
+ FROM knowledge_claims
141
+ WHERE projectId = ? AND status IN ('active', 'disputed') AND reviewState = 'approved'
142
+ GROUP BY conflictKey
143
+ HAVING COUNT(DISTINCT claimKey) > 1
144
+ ORDER BY conflictKey
145
+ `).all(projectId) as Array<{ conflictKey: string }>;
146
+ return rows.map(({ conflictKey }) => ({
147
+ conflictKey,
148
+ claims: this.listClaimsByConflictKey(projectId, conflictKey)
149
+ .filter(claim => claim.status === 'active' || claim.status === 'disputed'),
150
+ }));
151
+ }
152
+
153
+ insertClaim(claim: KnowledgeClaim): void {
154
+ this.db.prepare(`
155
+ INSERT INTO knowledge_claims (
156
+ id, projectId, subject, predicate, objectValue, scope, claimKey, conflictKey,
157
+ status, confidence, observedAt, validFrom, validTo, supersededBy,
158
+ reviewState, origin, createdAt, updatedAt
159
+ ) VALUES (
160
+ @id, @projectId, @subject, @predicate, @objectValue, @scope, @claimKey, @conflictKey,
161
+ @status, @confidence, @observedAt, @validFrom, @validTo, @supersededBy,
162
+ @reviewState, @origin, @createdAt, @updatedAt
163
+ )
164
+ `).run({
165
+ ...claim,
166
+ validFrom: claim.validFrom ?? null,
167
+ validTo: claim.validTo ?? null,
168
+ supersededBy: claim.supersededBy ?? null,
169
+ });
170
+ }
171
+
172
+ updateClaim(claim: KnowledgeClaim): void {
173
+ this.db.prepare(`
174
+ UPDATE knowledge_claims SET
175
+ subject = @subject,
176
+ predicate = @predicate,
177
+ objectValue = @objectValue,
178
+ scope = @scope,
179
+ claimKey = @claimKey,
180
+ conflictKey = @conflictKey,
181
+ status = @status,
182
+ confidence = @confidence,
183
+ observedAt = @observedAt,
184
+ validFrom = @validFrom,
185
+ validTo = @validTo,
186
+ supersededBy = @supersededBy,
187
+ reviewState = @reviewState,
188
+ origin = @origin,
189
+ updatedAt = @updatedAt
190
+ WHERE id = @id
191
+ `).run({
192
+ ...claim,
193
+ validFrom: claim.validFrom ?? null,
194
+ validTo: claim.validTo ?? null,
195
+ supersededBy: claim.supersededBy ?? null,
196
+ });
197
+ }
198
+
199
+ touchClaim(id: string, updatedAt: string): void {
200
+ this.db.prepare('UPDATE knowledge_claims SET updatedAt = ? WHERE id = ?').run(updatedAt, id);
201
+ }
202
+
203
+ insertEvidence(input: Omit<ClaimEvidenceRef, 'id' | 'createdAt'> & { createdAt?: string }): boolean {
204
+ const createdAt = input.createdAt ?? new Date().toISOString();
205
+ const key = evidenceKey(input);
206
+ const result = this.db.prepare(`
207
+ INSERT OR IGNORE INTO knowledge_claim_evidence (
208
+ id, claimId, evidenceKind, evidenceId, relation, snapshotId,
209
+ locator, capturedHash, evidenceKey, createdAt
210
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
211
+ `).run(
212
+ randomUUID(),
213
+ input.claimId,
214
+ input.evidenceKind,
215
+ input.evidenceId,
216
+ input.relation,
217
+ input.snapshotId ?? null,
218
+ input.locator ? sanitizeCredentials(input.locator) : null,
219
+ input.capturedHash ?? null,
220
+ key,
221
+ createdAt,
222
+ );
223
+ return Number(result.changes ?? 0) > 0;
224
+ }
225
+
226
+ listEvidence(claimId: string): ClaimEvidenceRef[] {
227
+ return this.db.prepare(`
228
+ SELECT * FROM knowledge_claim_evidence
229
+ WHERE claimId = ?
230
+ ORDER BY createdAt ASC, id
231
+ `).all(claimId).map(rowToEvidence);
232
+ }
233
+
234
+ recordEvent(input: Omit<ClaimEvent, 'id' | 'createdAt'> & { createdAt?: string }): ClaimEvent {
235
+ const event: ClaimEvent = {
236
+ id: randomUUID(),
237
+ ...input,
238
+ ...(input.detail ? { detail: sanitizeCredentials(input.detail) } : {}),
239
+ createdAt: input.createdAt ?? new Date().toISOString(),
240
+ };
241
+ this.db.prepare(`
242
+ INSERT INTO knowledge_claim_events (
243
+ id, projectId, claimId, kind, fromStatus, toStatus,
244
+ relatedClaimId, detail, createdAt
245
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
246
+ `).run(
247
+ event.id,
248
+ event.projectId,
249
+ event.claimId,
250
+ event.kind,
251
+ event.fromStatus ?? null,
252
+ event.toStatus ?? null,
253
+ event.relatedClaimId ?? null,
254
+ event.detail ?? null,
255
+ event.createdAt,
256
+ );
257
+ return event;
258
+ }
259
+
260
+ listEvents(claimId: string): ClaimEvent[] {
261
+ return this.db.prepare(`
262
+ SELECT * FROM knowledge_claim_events
263
+ WHERE claimId = ?
264
+ ORDER BY createdAt ASC, id
265
+ `).all(claimId).map(rowToEvent);
266
+ }
267
+ }