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.
Files changed (90) hide show
  1. package/CHANGELOG.md +75 -1
  2. package/README.md +12 -6
  3. package/README.zh-CN.md +12 -6
  4. package/dist/cli/index.js +2533 -585
  5. package/dist/index.d.ts +15 -8
  6. package/dist/index.js +1201 -284
  7. package/dist/maintenance-runner.js +387 -216
  8. package/dist/media-audio-runner.js +300 -129
  9. package/dist/media-video-runner.js +300 -129
  10. package/dist/memcode-runtime/CHANGELOG.md +75 -1
  11. package/dist/sdk.d.ts +1 -0
  12. package/dist/sdk.js +1882 -998
  13. package/dist/types.d.ts +1 -1
  14. package/dist/vector-backfill-runner.js +300 -129
  15. package/docs/AGENT_OPERATOR_PLAYBOOK.md +47 -3
  16. package/docs/API_REFERENCE.md +39 -1
  17. package/docs/CONFIGURATION.md +58 -1
  18. package/docs/DEVELOPMENT.md +1 -1
  19. package/docs/DOCKER.md +23 -3
  20. package/docs/INTEGRATIONS.md +1 -1
  21. package/docs/KNOWN_ISSUES_AND_ROADMAP.md +61 -1
  22. package/docs/MULTI-DEVICE-SYNC-RESEARCH.md +4 -0
  23. package/docs/PERFORMANCE.md +46 -2
  24. package/docs/README.md +2 -1
  25. package/docs/SETUP.md +56 -1
  26. package/package.json +3 -2
  27. package/plugins/antigravity/memorix/plugin.json +1 -1
  28. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  29. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  30. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  31. package/plugins/copilot/memorix/plugin.json +1 -1
  32. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  33. package/plugins/hermes/memorix/plugin.yaml +1 -1
  34. package/plugins/omp/memorix/package.json +1 -1
  35. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  36. package/plugins/pi/memorix/package.json +1 -1
  37. package/server.json +2 -2
  38. package/src/cli/capability-map.ts +1 -0
  39. package/src/cli/command-guide.ts +15 -3
  40. package/src/cli/commands/agent-integrations.ts +57 -0
  41. package/src/cli/commands/background.ts +11 -2
  42. package/src/cli/commands/context.ts +2 -0
  43. package/src/cli/commands/continuity.ts +155 -0
  44. package/src/cli/commands/init.ts +5 -2
  45. package/src/cli/commands/serve-http.ts +435 -84
  46. package/src/cli/commands/setup.ts +77 -10
  47. package/src/cli/commands/sync-store.ts +47 -12
  48. package/src/cli/commands/sync.ts +26 -0
  49. package/src/cli/index.ts +3 -1
  50. package/src/cli/tui/ConfigureView.tsx +2 -0
  51. package/src/codegraph/auto-context.ts +17 -0
  52. package/src/config/legacy-loader.ts +2 -2
  53. package/src/config/resolved-config.ts +8 -5
  54. package/src/config/yaml-loader.ts +20 -10
  55. package/src/dashboard/server.ts +10 -12
  56. package/src/embedding/api-provider.ts +4 -2
  57. package/src/embedding/fastembed-provider.ts +47 -14
  58. package/src/embedding/transformers-provider.ts +29 -7
  59. package/src/evaluation/memory-quality-gate.ts +90 -0
  60. package/src/hooks/official-skills.ts +2 -0
  61. package/src/integrations/registry.ts +1 -1
  62. package/src/knowledge/context-assembly.ts +1 -0
  63. package/src/knowledge/context-receipt.ts +15 -0
  64. package/src/knowledge/task-continuity.ts +413 -0
  65. package/src/knowledge/workset.ts +77 -0
  66. package/src/llm/provider.ts +2 -1
  67. package/src/memory/graph.ts +10 -8
  68. package/src/memory/session.ts +1 -1
  69. package/src/orchestrate/coordinator.ts +60 -0
  70. package/src/orchestrate/effect-ledger.ts +160 -0
  71. package/src/orchestrate/pipeline-trace.ts +40 -6
  72. package/src/runtime/maintenance-jobs.ts +12 -6
  73. package/src/sdk.ts +24 -0
  74. package/src/server/mcp-binding-store.ts +14 -3
  75. package/src/server/modern-mcp-bridge.ts +153 -27
  76. package/src/server/tool-profile.ts +3 -2
  77. package/src/server.ts +131 -7
  78. package/src/store/file-lock.ts +28 -3
  79. package/src/store/graph-store.ts +178 -46
  80. package/src/store/persistence.ts +14 -9
  81. package/src/store/sqlite-db.ts +150 -7
  82. package/src/store/sqlite-store.ts +32 -27
  83. package/src/sync/engine.ts +9 -7
  84. package/src/sync/namespace.ts +23 -0
  85. package/src/sync/policy.ts +19 -4
  86. package/src/sync/store-port.ts +43 -13
  87. package/src/sync/types.ts +2 -0
  88. package/src/team/team-store.ts +62 -27
  89. package/src/types.ts +1 -1
  90. package/src/workspace/mcp-adapters/grok.ts +32 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix-omp-package",
3
- "version": "1.9.4",
3
+ "version": "1.9.6",
4
4
  "description": "Memorix shared workspace memory package for Oh-my-Pi.",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.9.4",
3
+ "version": "1.9.6",
4
4
  "description": "Shared workspace memory for OpenClaw and other AI coding agents.",
5
5
  "author": {
6
6
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix-pi-package",
3
- "version": "1.9.4",
3
+ "version": "1.9.6",
4
4
  "description": "Memorix shared workspace memory package for Pi coding agent.",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
package/server.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "url": "https://github.com/AVIDS2/memorix",
8
8
  "source": "github"
9
9
  },
10
- "version": "1.9.4",
10
+ "version": "1.9.6",
11
11
  "websiteUrl": "https://mem.rglens.com",
12
12
  "icons": [
13
13
  {
@@ -22,7 +22,7 @@
22
22
  {
23
23
  "registryType": "npm",
24
24
  "identifier": "memorix",
25
- "version": "1.9.4",
25
+ "version": "1.9.6",
26
26
  "runtimeHint": "npx",
27
27
  "packageArguments": [
28
28
  {
@@ -21,6 +21,7 @@ export const CLI_NATIVE_PARITY: Record<string, string> = Object.freeze({
21
21
  memorix_media: 'memorix media import|attach|list|show|generate|status',
22
22
  memorix_evidence: 'memorix evidence list|get|sync|stale|events',
23
23
  memorix_feedback: 'memorix feedback record|show|audit',
24
+ memorix_continuity: 'memorix continuity start|record|show|list|close',
24
25
  memorix_dashboard: 'memorix dashboard',
25
26
  memorix_handoff: 'memorix handoff send',
26
27
  memorix_poll: 'memorix poll',
@@ -48,10 +48,22 @@ const GUIDES: Record<string, CliCommandGuide> = {
48
48
  context: {
49
49
  summary: 'Build the bounded task Workset used to resume or begin real work.',
50
50
  usage: [
51
- 'memorix context "continue the release fix" [--refresh auto|always|never]',
51
+ 'memorix context "continue the release fix" [--task-id <continuity-id>] [--refresh auto|always|never]',
52
52
  'memorix resume "continue the release fix" [--refresh auto|always|never]',
53
53
  ],
54
54
  },
55
+ continuity: {
56
+ summary: 'Track task requirements, decisions, verification, risks, and outcomes.',
57
+ usage: [
58
+ 'memorix continuity start --task "Harden the release path" [--requirements "a,b"]',
59
+ 'memorix continuity record --task-id <id> --kind decision --content "..."',
60
+ 'memorix continuity record --task-id <id> --kind verification --verification-status passed --content "..."',
61
+ 'memorix continuity show --task-id <id>',
62
+ 'memorix continuity close --task-id <id> --status completed --content "..."',
63
+ 'memorix continuity list',
64
+ ],
65
+ notes: ['Pass the returned task id to memorix context --task-id so the bounded brief includes the ledger.'],
66
+ },
55
67
  explain: {
56
68
  summary: 'Show why a task context contains its current facts and memory evidence.',
57
69
  usage: ['memorix explain [--refresh auto|always|never]'],
@@ -213,11 +225,11 @@ const GUIDES: Record<string, CliCommandGuide> = {
213
225
  'memorix sync rules --action status',
214
226
  'memorix sync workspace --action scan',
215
227
  'memorix sync store status --json',
216
- 'memorix sync store push|pull [--dry] [--json]',
228
+ 'memorix sync store push|pull [--dry] [--json] [--scope project|user]',
217
229
  'memorix sync store compact --dry --through device=sequence',
218
230
  'memorix sync store device rotate',
219
231
  ],
220
- notes: ['Store sync is opt-in, project-scoped, and never copies the live SQLite database.'],
232
+ notes: ['Store sync is opt-in and never copies the live SQLite database. Default scope is the current Git project; `--scope user` relays every local project.'],
221
233
  },
222
234
  ingest: {
223
235
  summary: 'Ingest Git and image evidence into project memory.',
@@ -168,6 +168,8 @@ function worstStatus(statuses: AgentIntegrationStatus[]): AgentIntegrationStatus
168
168
  }
169
169
 
170
170
  function isMcpConfigAgent(agent: AgentName): agent is McpConfigAgent {
171
+ // Grok has a setup-only HTTP adapter. Doctor/repair skip it so a default
172
+ // host-owned Grok MCP is not reported as missing.
171
173
  return agent !== 'pi' && agent !== 'grok';
172
174
  }
173
175
 
@@ -773,7 +775,62 @@ async function inspectCodexPluginMcp(): Promise<AgentMcpCheck | null> {
773
775
  }
774
776
  }
775
777
 
778
+ async function inspectOptionalGrokMcp(scope: AgentIntegrationScope): Promise<AgentIntegrationEntry['mcp']> {
779
+ if (scope === 'project' || scope === 'local') {
780
+ return { status: 'skipped', issues: ['mcp-host-owned'], checks: [] };
781
+ }
782
+
783
+ const adapter = getMcpAdapter('grok');
784
+ const configPath = adapter.getConfigPath();
785
+ if (!existsSync(configPath)) {
786
+ return {
787
+ status: 'skipped',
788
+ issues: ['mcp-host-owned'],
789
+ checks: [{
790
+ scope: 'global',
791
+ path: configPath,
792
+ exists: false,
793
+ status: 'skipped',
794
+ issues: ['mcp-host-owned'],
795
+ }],
796
+ };
797
+ }
798
+
799
+ const content = await readFile(configPath, 'utf-8');
800
+ const server = adapter.parse(content).find((entry) => entry.name === 'memorix');
801
+ if (!server) {
802
+ return {
803
+ status: 'skipped',
804
+ issues: ['mcp-host-owned'],
805
+ checks: [{
806
+ scope: 'global',
807
+ path: configPath,
808
+ exists: true,
809
+ status: 'skipped',
810
+ issues: ['mcp-host-owned'],
811
+ }],
812
+ };
813
+ }
814
+
815
+ const issues: string[] = [];
816
+ if (!server.url && looksLikeStaleMemorixCommand(server)) issues.push('stale-command-path');
817
+ if (!server.url && !isRecommendedStdioServer(server)) issues.push('nonstandard-mcp-command');
818
+ return {
819
+ status: issues.length > 0 ? 'repairable' : 'ok',
820
+ issues,
821
+ checks: [{
822
+ scope: 'global',
823
+ path: configPath,
824
+ exists: true,
825
+ status: issues.length > 0 ? 'repairable' : 'ok',
826
+ issues,
827
+ server: sanitizeServer(server),
828
+ }],
829
+ };
830
+ }
831
+
776
832
  async function inspectMcp(agent: AgentName, projectRoot: string, scope: AgentIntegrationScope): Promise<AgentIntegrationEntry['mcp']> {
833
+ if (agent === 'grok') return inspectOptionalGrokMcp(scope);
777
834
  if (!isMcpConfigAgent(agent)) {
778
835
  return { status: 'skipped', issues: ['mcp-managed-by-package'], checks: [] };
779
836
  }
@@ -570,8 +570,17 @@ async function doStartUnlocked(port: number): Promise<void> {
570
570
  if (healthy) {
571
571
  process.stderr.write('[OK] Control plane is running and healthy.\n');
572
572
  } else {
573
- process.stderr.write('[WARN] Health check timed out — service may still be initializing.\n');
574
- process.stderr.write(' Check later: memorix background status\n');
573
+ process.stderr.write('[ERROR] Health check timed out; refusing to leave an unverified background state.\n');
574
+ if (isProcessRunning(pid)) {
575
+ killProcess(pid);
576
+ for (let i = 0; i < 10 && isProcessRunning(pid); i++) {
577
+ await new Promise(r => setTimeout(r, 250));
578
+ }
579
+ }
580
+ clearState();
581
+ try { fs.unlinkSync(getMemorixDir() + '/background.ready'); } catch { /* best effort */ }
582
+ process.exitCode = 1;
583
+ return;
575
584
  }
576
585
 
577
586
  const footer = [
@@ -33,6 +33,7 @@ export default defineCommand({
33
33
  },
34
34
  args: {
35
35
  task: { type: 'string', description: 'Current task for context shaping' },
36
+ taskId: { type: 'string', description: 'Optional task continuity ledger id to include' },
36
37
  input: {
37
38
  type: 'positional',
38
39
  description: 'Current task for context shaping (ergonomic positional form)',
@@ -67,6 +68,7 @@ export default defineCommand({
67
68
  dataDir,
68
69
  observations: filterReadableObservations(getAllObservations(), reader),
69
70
  task,
71
+ taskId: (args.taskId as string | undefined)?.trim() || undefined,
70
72
  agent: coerceAgentTarget(args.agent as string | undefined),
71
73
  refresh: coerceRefreshMode(args.refresh as string | undefined),
72
74
  reader,
@@ -0,0 +1,155 @@
1
+ import { defineCommand } from 'citty';
2
+ import {
3
+ TaskContinuityStore,
4
+ type TaskContinuityEventKind,
5
+ type TaskContinuityOutcomeStatus,
6
+ type TaskContinuityVerificationStatus,
7
+ } from '../../knowledge/task-continuity.js';
8
+ import {
9
+ emitError,
10
+ emitResult,
11
+ getCliProjectContext,
12
+ parseCsvList,
13
+ } from './operator-shared.js';
14
+
15
+ const recordKinds: Array<Exclude<TaskContinuityEventKind, 'task' | 'outcome'>> = [
16
+ 'requirement',
17
+ 'decision',
18
+ 'verification',
19
+ 'risk',
20
+ ];
21
+
22
+ export default defineCommand({
23
+ meta: {
24
+ name: 'continuity',
25
+ description: 'Track task requirements, decisions, verification, risks, and outcomes',
26
+ },
27
+ args: {
28
+ taskId: { type: 'string', description: 'Continuity task id' },
29
+ task: { type: 'string', description: 'Task description for start' },
30
+ requirements: { type: 'string', description: 'Comma-separated requirements for start' },
31
+ kind: { type: 'string', description: 'requirement, decision, verification, or risk' },
32
+ content: { type: 'string', description: 'Entry or outcome text' },
33
+ verificationStatus: { type: 'string', description: 'pending, passed, failed, or skipped' },
34
+ verificationId: { type: 'string', description: 'Stable verification obligation id for status transitions' },
35
+ idempotencyKey: { type: 'string', description: 'Stable retry key for idempotent writes' },
36
+ status: { type: 'string', description: 'completed, blocked, or abandoned for close' },
37
+ source: { type: 'string', description: 'Source or evidence reference' },
38
+ evidence: { type: 'string', description: 'Comma-separated evidence references' },
39
+ actor: { type: 'string', description: 'Actor id or label' },
40
+ limit: { type: 'string', description: 'Maximum ledgers for list' },
41
+ json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
42
+ },
43
+ run: async ({ args }) => {
44
+ const action = (args._ as string[])?.[0] || '';
45
+ const asJson = !!args.json;
46
+ try {
47
+ const { project, dataDir, identity } = await getCliProjectContext({ searchIndex: false });
48
+ const store = new TaskContinuityStore();
49
+ await store.init(dataDir);
50
+ const actor = String(args.actor || identity?.agentId || '').trim() || undefined;
51
+ if (action === 'start') {
52
+ if (!args.task) {
53
+ emitError('task is required for "memorix continuity start"', asJson);
54
+ return;
55
+ }
56
+ const result = store.start({
57
+ projectId: project.id,
58
+ task: String(args.task),
59
+ ...(args.taskId ? { taskId: String(args.taskId) } : {}),
60
+ requirements: parseCsvList(args.requirements as string | undefined),
61
+ ...(actor ? { actor } : {}),
62
+ ...(args.idempotencyKey ? { idempotencyKey: String(args.idempotencyKey) } : {}),
63
+ });
64
+ emitResult({ project, ...result }, 'Continuity started: ' + result.taskId, asJson);
65
+ return;
66
+ }
67
+ if (action === 'list') {
68
+ const ledgers = store.list(project.id, Number(args.limit || 20));
69
+ emitResult(
70
+ { project, ledgers },
71
+ ledgers.length
72
+ ? ledgers.map(item => item.status + ': ' + item.taskId + ' ' + item.task).join('\n')
73
+ : 'No continuity ledgers found.',
74
+ asJson,
75
+ );
76
+ return;
77
+ }
78
+ const taskId = String(args.taskId || '').trim();
79
+ if (!taskId) {
80
+ emitError('taskId is required for this continuity action.', asJson);
81
+ return;
82
+ }
83
+ if (action === 'show') {
84
+ const ledger = store.get(project.id, taskId);
85
+ if (!ledger) {
86
+ emitError('Task continuity ledger not found.', asJson);
87
+ return;
88
+ }
89
+ emitResult({ project, ledger }, JSON.stringify(ledger, null, 2), asJson);
90
+ return;
91
+ }
92
+ if (action === 'record') {
93
+ const kind = String(args.kind || '').trim() as Exclude<TaskContinuityEventKind, 'task' | 'outcome'>;
94
+ const content = String(args.content || '').trim();
95
+ if (!recordKinds.includes(kind) || !content) {
96
+ emitError('record requires --kind (requirement, decision, verification, risk) and --content.', asJson);
97
+ return;
98
+ }
99
+ const verificationStatus = args.verificationStatus
100
+ ? String(args.verificationStatus).trim() as TaskContinuityVerificationStatus
101
+ : undefined;
102
+ if (verificationStatus && !['pending', 'passed', 'failed', 'skipped'].includes(verificationStatus)) {
103
+ emitError('verificationStatus must be pending, passed, failed, or skipped.', asJson);
104
+ return;
105
+ }
106
+ const result = store.record({
107
+ projectId: project.id,
108
+ taskId,
109
+ kind,
110
+ content,
111
+ ...(verificationStatus ? { verificationStatus } : {}),
112
+ ...(args.source ? { sourceRef: String(args.source) } : {}),
113
+ evidenceRefs: parseCsvList(args.evidence as string | undefined),
114
+ ...(actor ? { actor } : {}),
115
+ ...(args.verificationId ? { verificationId: String(args.verificationId) } : {}),
116
+ ...(args.idempotencyKey ? { idempotencyKey: String(args.idempotencyKey) } : {}),
117
+ });
118
+ emitResult({ project, ...result }, 'Continuity recorded: ' + kind, asJson);
119
+ return;
120
+ }
121
+ if (action === 'close') {
122
+ const status = String(args.status || '').trim() as TaskContinuityOutcomeStatus;
123
+ const content = String(args.content || '').trim();
124
+ if (!['completed', 'blocked', 'abandoned'].includes(status) || !content) {
125
+ emitError('close requires --status (completed, blocked, abandoned) and --content.', asJson);
126
+ return;
127
+ }
128
+ const result = store.close({
129
+ projectId: project.id,
130
+ taskId,
131
+ status,
132
+ content,
133
+ ...(args.source ? { sourceRef: String(args.source) } : {}),
134
+ evidenceRefs: parseCsvList(args.evidence as string | undefined),
135
+ ...(actor ? { actor } : {}),
136
+ ...(args.idempotencyKey ? { idempotencyKey: String(args.idempotencyKey) } : {}),
137
+ });
138
+ emitResult({ project, ...result }, 'Continuity closed: ' + status, asJson);
139
+ return;
140
+ }
141
+ console.log('Memorix Task Continuity');
142
+ console.log('');
143
+ console.log('Usage:');
144
+ console.log(' memorix continuity start --task "..." [--requirements "a,b"]');
145
+ console.log(' memorix continuity record --taskId <id> --kind decision --content "..."');
146
+ console.log(' memorix continuity record --taskId <id> --kind verification --verificationStatus passed --content "..."');
147
+ console.log(' Use --verificationId <id> to transition an existing verification obligation.');
148
+ console.log(' memorix continuity show --taskId <id>');
149
+ console.log(' memorix continuity list [--limit 20]');
150
+ console.log(' memorix continuity close --taskId <id> --status completed --content "..."');
151
+ } catch (error) {
152
+ emitError(error instanceof Error ? error.message : String(error), asJson);
153
+ }
154
+ },
155
+ });
@@ -21,7 +21,7 @@ import {
21
21
  type InitScope,
22
22
  } from './init-shared.js';
23
23
 
24
- type InitLlmProvider = 'none' | 'openai' | 'anthropic' | 'openrouter' | 'custom';
24
+ type InitLlmProvider = 'none' | 'openai' | 'anthropic' | 'openrouter' | 'atlascloud' | 'custom';
25
25
  type InitEmbeddingProvider = 'off' | 'api' | 'fastembed';
26
26
  type InitInjectMode = 'minimal' | 'full' | 'silent';
27
27
 
@@ -103,6 +103,7 @@ export default defineCommand({
103
103
  { value: 'openai', label: 'OpenAI', hint: 'gpt-4o-mini' },
104
104
  { value: 'anthropic', label: 'Anthropic', hint: 'claude-3-haiku' },
105
105
  { value: 'openrouter', label: 'OpenRouter', hint: 'multi-provider' },
106
+ { value: 'atlascloud', label: 'Atlas Cloud', hint: 'DeepSeek V3.2' },
106
107
  { value: 'custom', label: 'Custom', hint: 'OpenAI-compatible endpoint' },
107
108
  ],
108
109
  });
@@ -162,7 +163,7 @@ export default defineCommand({
162
163
 
163
164
  if (llmProvider !== 'none') {
164
165
  envLines.push('# LLM API key');
165
- if (llmProvider === 'openai' || llmProvider === 'custom') {
166
+ if (llmProvider === 'openai' || llmProvider === 'custom' || llmProvider === 'atlascloud') {
166
167
  envLines.push('MEMORIX_LLM_API_KEY=sk-your-key-here');
167
168
  } else if (llmProvider === 'anthropic') {
168
169
  envLines.push('MEMORIX_LLM_API_KEY=sk-ant-your-key-here');
@@ -292,6 +293,8 @@ export function buildInitTomlConfig(options: {
292
293
  lines.push('model = "claude-3-haiku-20240307"');
293
294
  } else if (options.llmProvider === 'openrouter') {
294
295
  lines.push('# model = "openai/gpt-4o-mini"');
296
+ } else if (options.llmProvider === 'atlascloud') {
297
+ lines.push('model = "deepseek-ai/deepseek-v3.2"');
295
298
  }
296
299
  if (options.llmProvider === 'custom') {
297
300
  lines.push('# OpenAI-compatible endpoint, for example DashScope, DeepSeek, Ollama, or an internal gateway.');