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.
- package/CHANGELOG.md +33 -1
- package/README.md +6 -3
- package/README.zh-CN.md +6 -3
- package/dist/cli/index.js +36639 -31279
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/static/app.js +50 -30
- package/dist/index.js +6636 -1778
- package/dist/index.js.map +1 -1
- package/dist/maintenance-runner.d.ts +1 -1
- package/dist/maintenance-runner.js +3775 -302
- package/dist/maintenance-runner.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +33 -1
- package/dist/sdk.d.ts +1 -1
- package/dist/sdk.js +6634 -1776
- package/dist/sdk.js.map +1 -1
- package/docs/1.2.0-CLAIM-LEDGER.md +72 -0
- package/docs/1.2.0-CODE-STATE.md +61 -0
- package/docs/1.2.0-DEVELOPMENT-CHARTER.md +255 -0
- package/docs/1.2.0-DYNAMIC-LIFECYCLE.md +71 -0
- package/docs/1.2.0-EVALUATION-HARNESS.md +51 -0
- package/docs/1.2.0-IMPLEMENTATION-PLAN.md +554 -0
- package/docs/1.2.0-KNOWLEDGE-WORKFLOW-RESEARCH.md +205 -0
- package/docs/1.2.0-KNOWLEDGE-WORKSPACE.md +68 -0
- package/docs/1.2.0-PRODUCT-STORY.md +234 -0
- package/docs/1.2.0-PROVIDER-QUALITY.md +189 -0
- package/docs/1.2.0-WORKFLOW-INHERITANCE.md +101 -0
- package/docs/1.2.0-WORKSET-RETRIEVAL.md +80 -0
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +6 -3
- package/docs/API_REFERENCE.md +27 -6
- package/docs/CONFIGURATION.md +21 -3
- package/docs/DEVELOPMENT.md +4 -0
- package/docs/README.md +17 -2
- package/docs/SETUP.md +7 -1
- package/docs/dev-log/progress.txt +120 -40
- package/docs/knowledge/workflows/memorix-release.md +57 -0
- package/llms-full.txt +16 -2
- package/llms.txt +9 -4
- package/package.json +3 -2
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/src/cli/capability-map.ts +1 -0
- package/src/cli/commands/codegraph.ts +111 -9
- package/src/cli/commands/context.ts +2 -0
- package/src/cli/commands/doctor.ts +73 -4
- package/src/cli/commands/knowledge.ts +322 -0
- package/src/cli/commands/serve-http.ts +26 -42
- package/src/cli/commands/setup.ts +9 -3
- package/src/cli/index.ts +3 -1
- package/src/cli/tui/App.tsx +1 -1
- package/src/cli/tui/Panels.tsx +8 -8
- package/src/cli/tui/theme.ts +1 -1
- package/src/cli/tui/views/GraphView.tsx +8 -7
- package/src/cli/tui/views/KnowledgeView.tsx +9 -9
- package/src/codegraph/auto-context.ts +169 -19
- package/src/codegraph/code-state.ts +95 -0
- package/src/codegraph/context-pack.ts +82 -1
- package/src/codegraph/current-facts.ts +19 -1
- package/src/codegraph/external-provider.ts +581 -0
- package/src/codegraph/lite-provider.ts +64 -19
- package/src/codegraph/project-context.ts +9 -1
- package/src/codegraph/store.ts +154 -6
- package/src/codegraph/task-lens.ts +49 -5
- package/src/codegraph/types.ts +117 -0
- package/src/config/resolved-config.ts +28 -0
- package/src/config/toml-loader.ts +3 -0
- package/src/config/yaml-loader.ts +6 -0
- package/src/dashboard/server.ts +30 -47
- package/src/evaluation/workset-evaluation.ts +120 -0
- package/src/hooks/handler.ts +48 -6
- package/src/knowledge/claim-store.ts +267 -0
- package/src/knowledge/claims.ts +587 -0
- package/src/knowledge/markdown.ts +129 -0
- package/src/knowledge/types.ts +158 -0
- package/src/knowledge/wiki.ts +524 -0
- package/src/knowledge/workflow-store.ts +168 -0
- package/src/knowledge/workflow-types.ts +95 -0
- package/src/knowledge/workflows.ts +774 -0
- package/src/knowledge/workset.ts +515 -0
- package/src/knowledge/workspace-store.ts +220 -0
- package/src/knowledge/workspace-types.ts +106 -0
- package/src/knowledge/workspace.ts +220 -0
- package/src/memory/auto-relations.ts +21 -0
- package/src/memory/graph-scope.ts +46 -0
- package/src/memory/observations.ts +19 -0
- package/src/orchestrate/verify-gate.ts +33 -10
- package/src/runtime/control-plane-maintenance.ts +5 -0
- package/src/runtime/isolated-maintenance.ts +5 -0
- package/src/runtime/lifecycle-status.ts +102 -0
- package/src/runtime/lifecycle.ts +107 -0
- package/src/runtime/maintenance-jobs.ts +5 -0
- package/src/runtime/project-maintenance.ts +190 -0
- package/src/server/tool-profile.ts +3 -2
- package/src/server.ts +424 -22
- package/src/store/file-lock.ts +24 -4
- package/src/store/sqlite-db.ts +307 -0
- package/src/wiki/generator.ts +4 -2
- package/src/wiki/knowledge-graph.ts +7 -4
- package/src/wiki/types.ts +16 -4
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { promises as fs } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import matter from 'gray-matter';
|
|
5
|
+
import { atomicWriteFile } from '../store/file-lock.js';
|
|
6
|
+
import type { KnowledgePage, KnowledgePageFrontmatter, KnowledgePageKind, KnowledgePageReviewState, KnowledgePageStatus } from './workspace-types.js';
|
|
7
|
+
|
|
8
|
+
const PAGE_KINDS: KnowledgePageKind[] = ['topic', 'decision', 'risk', 'index', 'schema', 'log'];
|
|
9
|
+
const PAGE_STATUSES: KnowledgePageStatus[] = ['active', 'proposed'];
|
|
10
|
+
const REVIEW_STATES: KnowledgePageReviewState[] = ['approved', 'needs-review'];
|
|
11
|
+
const LINK_PATTERN = /\[[^\]]+\]\(([^)]+)\)/g;
|
|
12
|
+
|
|
13
|
+
function hashContent(content: string): string {
|
|
14
|
+
return createHash('sha256').update(content).digest('hex');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function stringField(data: Record<string, unknown>, key: string): string {
|
|
18
|
+
const value = data[key];
|
|
19
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
20
|
+
throw new Error('missing required field ' + key);
|
|
21
|
+
}
|
|
22
|
+
return value.trim();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function stringArray(data: Record<string, unknown>, key: string): string[] {
|
|
26
|
+
const value = data[key];
|
|
27
|
+
if (!Array.isArray(value) || value.some(item => typeof item !== 'string')) {
|
|
28
|
+
throw new Error('field ' + key + ' must be a string array');
|
|
29
|
+
}
|
|
30
|
+
return value.map(item => item.trim()).filter(Boolean);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function optionalString(data: Record<string, unknown>, key: string): string | undefined {
|
|
34
|
+
const value = data[key];
|
|
35
|
+
if (value === undefined || value === null || value === '') return undefined;
|
|
36
|
+
if (typeof value !== 'string') throw new Error('field ' + key + ' must be text');
|
|
37
|
+
return value.trim() || undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function validateKnowledgePageFrontmatter(data: Record<string, unknown>): KnowledgePageFrontmatter {
|
|
41
|
+
const kind = stringField(data, 'kind') as KnowledgePageKind;
|
|
42
|
+
const status = stringField(data, 'status') as KnowledgePageStatus;
|
|
43
|
+
const reviewState = stringField(data, 'reviewState') as KnowledgePageReviewState;
|
|
44
|
+
if (!PAGE_KINDS.includes(kind)) throw new Error('field kind is invalid');
|
|
45
|
+
if (!PAGE_STATUSES.includes(status)) throw new Error('field status is invalid');
|
|
46
|
+
if (!REVIEW_STATES.includes(reviewState)) throw new Error('field reviewState is invalid');
|
|
47
|
+
return {
|
|
48
|
+
id: stringField(data, 'id'),
|
|
49
|
+
title: stringField(data, 'title'),
|
|
50
|
+
kind,
|
|
51
|
+
status,
|
|
52
|
+
reviewState,
|
|
53
|
+
claimIds: stringArray(data, 'claimIds'),
|
|
54
|
+
evidenceRefs: stringArray(data, 'evidenceRefs'),
|
|
55
|
+
...(optionalString(data, 'snapshotId') ? { snapshotId: optionalString(data, 'snapshotId') } : {}),
|
|
56
|
+
tags: stringArray(data, 'tags'),
|
|
57
|
+
sourceHash: stringField(data, 'sourceHash'),
|
|
58
|
+
generatedAt: stringField(data, 'generatedAt'),
|
|
59
|
+
updatedAt: stringField(data, 'updatedAt'),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function extractInternalMarkdownLinks(body: string): string[] {
|
|
64
|
+
const links = new Set<string>();
|
|
65
|
+
for (const match of body.matchAll(LINK_PATTERN)) {
|
|
66
|
+
const raw = match[1].trim();
|
|
67
|
+
if (!raw || raw.startsWith('#') || /^[a-z][a-z0-9+.-]*:/i.test(raw)) continue;
|
|
68
|
+
const pathPart = raw.split('#', 1)[0].replace(/\\/g, '/');
|
|
69
|
+
if (!pathPart.endsWith('.md')) continue;
|
|
70
|
+
links.add(pathPart);
|
|
71
|
+
}
|
|
72
|
+
return [...links].sort();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function resolvePageLink(sourceRelativePath: string, target: string): string | undefined {
|
|
76
|
+
const sourceDir = path.posix.dirname(sourceRelativePath.replace(/\\/g, '/'));
|
|
77
|
+
const normalized = path.posix.normalize(path.posix.join(sourceDir, target.replace(/\\/g, '/')));
|
|
78
|
+
if (!normalized || normalized === '.' || normalized === '..' || normalized.startsWith('../')) return undefined;
|
|
79
|
+
return normalized;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function renderKnowledgePage(frontmatter: KnowledgePageFrontmatter, body: string): string {
|
|
83
|
+
const normalizedBody = body.trimEnd() + '\n';
|
|
84
|
+
return matter.stringify(normalizedBody, {
|
|
85
|
+
id: frontmatter.id,
|
|
86
|
+
title: frontmatter.title,
|
|
87
|
+
kind: frontmatter.kind,
|
|
88
|
+
status: frontmatter.status,
|
|
89
|
+
reviewState: frontmatter.reviewState,
|
|
90
|
+
claimIds: frontmatter.claimIds,
|
|
91
|
+
evidenceRefs: frontmatter.evidenceRefs,
|
|
92
|
+
...(frontmatter.snapshotId ? { snapshotId: frontmatter.snapshotId } : {}),
|
|
93
|
+
tags: frontmatter.tags,
|
|
94
|
+
sourceHash: frontmatter.sourceHash,
|
|
95
|
+
generatedAt: frontmatter.generatedAt,
|
|
96
|
+
updatedAt: frontmatter.updatedAt,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export async function readKnowledgePage(absolutePath: string, workspaceRoot?: string): Promise<KnowledgePage> {
|
|
101
|
+
const raw = await fs.readFile(absolutePath, 'utf8');
|
|
102
|
+
try {
|
|
103
|
+
const parsed = matter(raw);
|
|
104
|
+
const frontmatter = validateKnowledgePageFrontmatter(parsed.data as Record<string, unknown>);
|
|
105
|
+
return {
|
|
106
|
+
absolutePath,
|
|
107
|
+
relativePath: workspaceRoot
|
|
108
|
+
? path.relative(workspaceRoot, absolutePath).split(path.sep).join('/')
|
|
109
|
+
: path.basename(absolutePath),
|
|
110
|
+
frontmatter,
|
|
111
|
+
body: parsed.content.trim(),
|
|
112
|
+
contentHash: hashContent(raw),
|
|
113
|
+
links: extractInternalMarkdownLinks(parsed.content),
|
|
114
|
+
};
|
|
115
|
+
} catch (error) {
|
|
116
|
+
const detail = error instanceof Error ? error.message : 'invalid page data';
|
|
117
|
+
throw new Error('Malformed knowledge page frontmatter in ' + absolutePath + ': ' + detail);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export async function writeKnowledgePage(absolutePath: string, content: string): Promise<string> {
|
|
122
|
+
await fs.mkdir(path.dirname(absolutePath), { recursive: true });
|
|
123
|
+
await atomicWriteFile(absolutePath, content);
|
|
124
|
+
return hashContent(content);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function pageContentHash(content: string): string {
|
|
128
|
+
return hashContent(content);
|
|
129
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable, source-qualified knowledge contracts.
|
|
3
|
+
*
|
|
4
|
+
* Claims are deliberately smaller than a wiki page: they record one assertion,
|
|
5
|
+
* its lifecycle, and the evidence that makes the assertion safe to retrieve.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const KNOWLEDGE_CLAIM_SCOPES = [
|
|
9
|
+
'project',
|
|
10
|
+
'workspace',
|
|
11
|
+
'team',
|
|
12
|
+
'workflow',
|
|
13
|
+
'task',
|
|
14
|
+
] as const;
|
|
15
|
+
|
|
16
|
+
export type KnowledgeClaimScope = typeof KNOWLEDGE_CLAIM_SCOPES[number];
|
|
17
|
+
|
|
18
|
+
export const KNOWLEDGE_CLAIM_STATUSES = [
|
|
19
|
+
'active',
|
|
20
|
+
'superseded',
|
|
21
|
+
'disputed',
|
|
22
|
+
'unknown',
|
|
23
|
+
] as const;
|
|
24
|
+
|
|
25
|
+
export type KnowledgeClaimStatus = typeof KNOWLEDGE_CLAIM_STATUSES[number];
|
|
26
|
+
|
|
27
|
+
export const KNOWLEDGE_CLAIM_REVIEW_STATES = [
|
|
28
|
+
'approved',
|
|
29
|
+
'needs-review',
|
|
30
|
+
'draft',
|
|
31
|
+
'rejected',
|
|
32
|
+
] as const;
|
|
33
|
+
|
|
34
|
+
export type KnowledgeClaimReviewState = typeof KNOWLEDGE_CLAIM_REVIEW_STATES[number];
|
|
35
|
+
|
|
36
|
+
export const CLAIM_EVIDENCE_KINDS = [
|
|
37
|
+
'observation',
|
|
38
|
+
'git',
|
|
39
|
+
'code',
|
|
40
|
+
'test',
|
|
41
|
+
'document',
|
|
42
|
+
'workflow',
|
|
43
|
+
'run',
|
|
44
|
+
] as const;
|
|
45
|
+
|
|
46
|
+
export type ClaimEvidenceKind = typeof CLAIM_EVIDENCE_KINDS[number];
|
|
47
|
+
|
|
48
|
+
export const CLAIM_EVIDENCE_RELATIONS = [
|
|
49
|
+
'supports',
|
|
50
|
+
'contradicts',
|
|
51
|
+
'derives',
|
|
52
|
+
'verifies',
|
|
53
|
+
] as const;
|
|
54
|
+
|
|
55
|
+
export type ClaimEvidenceRelation = typeof CLAIM_EVIDENCE_RELATIONS[number];
|
|
56
|
+
|
|
57
|
+
export type KnowledgeClaimOrigin = 'explicit' | 'git' | 'derived' | 'model';
|
|
58
|
+
|
|
59
|
+
export const CLAIM_EVENT_KINDS = [
|
|
60
|
+
'created',
|
|
61
|
+
'evidence-added',
|
|
62
|
+
'reviewed',
|
|
63
|
+
'conflicted',
|
|
64
|
+
'superseded',
|
|
65
|
+
'requalified',
|
|
66
|
+
] as const;
|
|
67
|
+
|
|
68
|
+
export type ClaimEventKind = typeof CLAIM_EVENT_KINDS[number];
|
|
69
|
+
|
|
70
|
+
export interface KnowledgeClaim {
|
|
71
|
+
id: string;
|
|
72
|
+
projectId: string;
|
|
73
|
+
subject: string;
|
|
74
|
+
predicate: string;
|
|
75
|
+
objectValue: string;
|
|
76
|
+
scope: KnowledgeClaimScope;
|
|
77
|
+
/** Exact normalized assertion identity: subject + predicate + object + scope. */
|
|
78
|
+
claimKey: string;
|
|
79
|
+
/** Normalized competing-assertion identity: subject + predicate + scope. */
|
|
80
|
+
conflictKey: string;
|
|
81
|
+
status: KnowledgeClaimStatus;
|
|
82
|
+
confidence: number;
|
|
83
|
+
observedAt: string;
|
|
84
|
+
validFrom?: string;
|
|
85
|
+
validTo?: string;
|
|
86
|
+
supersededBy?: string;
|
|
87
|
+
reviewState: KnowledgeClaimReviewState;
|
|
88
|
+
origin: KnowledgeClaimOrigin;
|
|
89
|
+
createdAt: string;
|
|
90
|
+
updatedAt: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface ClaimEvidenceRef {
|
|
94
|
+
id: string;
|
|
95
|
+
claimId: string;
|
|
96
|
+
evidenceKind: ClaimEvidenceKind;
|
|
97
|
+
evidenceId: string;
|
|
98
|
+
relation: ClaimEvidenceRelation;
|
|
99
|
+
snapshotId?: string;
|
|
100
|
+
locator?: string;
|
|
101
|
+
capturedHash?: string;
|
|
102
|
+
createdAt: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface ClaimEvidenceInput {
|
|
106
|
+
evidenceKind: ClaimEvidenceKind;
|
|
107
|
+
evidenceId: string;
|
|
108
|
+
relation: ClaimEvidenceRelation;
|
|
109
|
+
snapshotId?: string;
|
|
110
|
+
locator?: string;
|
|
111
|
+
capturedHash?: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface KnowledgeClaimInput {
|
|
115
|
+
projectId: string;
|
|
116
|
+
subject: string;
|
|
117
|
+
predicate: string;
|
|
118
|
+
objectValue: string;
|
|
119
|
+
scope: KnowledgeClaimScope;
|
|
120
|
+
evidence: ClaimEvidenceInput[];
|
|
121
|
+
confidence?: number;
|
|
122
|
+
observedAt?: string;
|
|
123
|
+
validFrom?: string;
|
|
124
|
+
validTo?: string;
|
|
125
|
+
status?: KnowledgeClaimStatus;
|
|
126
|
+
reviewState?: KnowledgeClaimReviewState;
|
|
127
|
+
origin?: KnowledgeClaimOrigin;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface ClaimEvent {
|
|
131
|
+
id: string;
|
|
132
|
+
projectId: string;
|
|
133
|
+
claimId: string;
|
|
134
|
+
kind: ClaimEventKind;
|
|
135
|
+
fromStatus?: KnowledgeClaimStatus;
|
|
136
|
+
toStatus?: KnowledgeClaimStatus;
|
|
137
|
+
relatedClaimId?: string;
|
|
138
|
+
detail?: string;
|
|
139
|
+
createdAt: string;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface ClaimConflict {
|
|
143
|
+
conflictKey: string;
|
|
144
|
+
claims: KnowledgeClaim[];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface ClaimWriteResult {
|
|
148
|
+
claim: KnowledgeClaim;
|
|
149
|
+
created: boolean;
|
|
150
|
+
conflicts: ClaimConflict[];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface ClaimSelection {
|
|
154
|
+
claims: KnowledgeClaim[];
|
|
155
|
+
cautions: string[];
|
|
156
|
+
tokenCount: number;
|
|
157
|
+
reasons: Record<string, string>;
|
|
158
|
+
}
|