memorix 1.8.0 → 1.8.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 +9 -0
- package/dist/cli/index.js +83 -80
- package/dist/dashboard/static/app.js +37 -23
- package/dist/dashboard/static/index.html +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +54 -78
- package/dist/memcode-runtime/CHANGELOG.md +9 -0
- package/dist/sdk.js +53 -77
- package/docs/API_REFERENCE.md +2 -0
- package/docs/ARCHITECTURE.md +3 -1
- package/docs/DASHBOARD.md +6 -0
- package/docs/DEVELOPMENT.md +2 -2
- package/docs/README.md +5 -2
- package/package.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/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/commands/dashboard.ts +1 -1
- package/src/cli/commands/serve-http.ts +28 -1
- package/src/dashboard/server.ts +85 -86
- package/src/dashboard/static/index.html +1 -1
- package/src/server.ts +4 -4
|
@@ -889,6 +889,15 @@ export default defineCommand({
|
|
|
889
889
|
}
|
|
890
890
|
|
|
891
891
|
if (apiPath === '/team') {
|
|
892
|
+
if (req.method !== 'GET') {
|
|
893
|
+
sendJson({
|
|
894
|
+
status: 'error',
|
|
895
|
+
readOnly: true,
|
|
896
|
+
error: 'Coordination status is read-only',
|
|
897
|
+
errorCode: 'COORDINATION_STATUS_READ_ONLY',
|
|
898
|
+
}, 405);
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
892
901
|
// Phase 4a: All team state is in SQLite via TeamStore
|
|
893
902
|
const { projectId: teamProjectId, dataDir: teamDataDir } = await resolveRequestProject(url);
|
|
894
903
|
const ts = await getTeamStore(teamDataDir);
|
|
@@ -967,8 +976,9 @@ export default defineCommand({
|
|
|
967
976
|
const openHandoffs = handoffs.filter((handoff: any) => handoff.status !== 'completed').length;
|
|
968
977
|
|
|
969
978
|
sendJson({
|
|
979
|
+
status: 'ok',
|
|
970
980
|
mode: 'control-plane',
|
|
971
|
-
readOnly:
|
|
981
|
+
readOnly: true,
|
|
972
982
|
scope,
|
|
973
983
|
agents: withTier,
|
|
974
984
|
// Primary headline number
|
|
@@ -1650,6 +1660,23 @@ export default defineCommand({
|
|
|
1650
1660
|
|
|
1651
1661
|
sendJson({ error: 'Not found' }, 404);
|
|
1652
1662
|
} catch (err) {
|
|
1663
|
+
if (apiPath === '/team') {
|
|
1664
|
+
const scope = url.searchParams.get('scope') === 'global' ? 'global' : 'project';
|
|
1665
|
+
console.error('[memorix] coordination snapshot unavailable', { status: 'error', scope });
|
|
1666
|
+
sendJson({
|
|
1667
|
+
status: 'error',
|
|
1668
|
+
mode: 'control-plane',
|
|
1669
|
+
readOnly: true,
|
|
1670
|
+
scope,
|
|
1671
|
+
agents: [],
|
|
1672
|
+
locks: [],
|
|
1673
|
+
tasks: [],
|
|
1674
|
+
handoffs: [],
|
|
1675
|
+
error: 'Coordination state unavailable',
|
|
1676
|
+
errorCode: 'COORDINATION_STATE_UNAVAILABLE',
|
|
1677
|
+
}, 503);
|
|
1678
|
+
return;
|
|
1679
|
+
}
|
|
1653
1680
|
sendJson({ error: err instanceof Error ? err.message : 'Unknown error' }, 500);
|
|
1654
1681
|
}
|
|
1655
1682
|
}
|
package/src/dashboard/server.ts
CHANGED
|
@@ -958,18 +958,10 @@ interface DashboardState {
|
|
|
958
958
|
dataDir: string;
|
|
959
959
|
projectRoot: string | null;
|
|
960
960
|
projectResolved: boolean;
|
|
961
|
-
mode: 'standalone'
|
|
961
|
+
mode: 'standalone';
|
|
962
962
|
port: number;
|
|
963
963
|
}
|
|
964
964
|
|
|
965
|
-
/** Optional coordination instances passed from MCP server */
|
|
966
|
-
export interface TeamInstances {
|
|
967
|
-
registry: { listAgents: (filter?: any) => any[]; getActiveCount: () => number; getAgent: (id: string) => any };
|
|
968
|
-
fileLocks: { listLocks: (agentId?: string) => any[]; cleanExpired: () => void };
|
|
969
|
-
taskManager: { list: (filter?: any) => any[]; getAvailable: () => any[] };
|
|
970
|
-
messageBus: { getUnreadCount: (agentId: string) => number };
|
|
971
|
-
}
|
|
972
|
-
|
|
973
965
|
function parseJsonField(value: unknown, fallback: unknown): unknown {
|
|
974
966
|
if (typeof value !== 'string') return value ?? fallback;
|
|
975
967
|
try {
|
|
@@ -1027,7 +1019,72 @@ function normalizeDashboardTask(task: any) {
|
|
|
1027
1019
|
};
|
|
1028
1020
|
}
|
|
1029
1021
|
|
|
1030
|
-
|
|
1022
|
+
type TeamSnapshotStatus = 'ok' | 'degraded' | 'error';
|
|
1023
|
+
|
|
1024
|
+
interface TeamSnapshot {
|
|
1025
|
+
status: TeamSnapshotStatus;
|
|
1026
|
+
mode: DashboardState['mode'];
|
|
1027
|
+
readOnly: true;
|
|
1028
|
+
scope: string;
|
|
1029
|
+
agents: any[];
|
|
1030
|
+
activeCount: number;
|
|
1031
|
+
recentCount: number;
|
|
1032
|
+
historicalCount: number;
|
|
1033
|
+
totalAgents: number;
|
|
1034
|
+
recentWindowDays: number;
|
|
1035
|
+
locks: any[];
|
|
1036
|
+
tasks: any[];
|
|
1037
|
+
availableTasks: number;
|
|
1038
|
+
sessions: number;
|
|
1039
|
+
roles: any[];
|
|
1040
|
+
roleOccupancy: any[];
|
|
1041
|
+
handoffs: any[];
|
|
1042
|
+
openTasks: number;
|
|
1043
|
+
openHandoffs: number;
|
|
1044
|
+
totalUnread: number;
|
|
1045
|
+
activeSessions: number;
|
|
1046
|
+
error?: string;
|
|
1047
|
+
errorCode?: string;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
function emptyTeamSnapshot(
|
|
1051
|
+
mode: DashboardState['mode'],
|
|
1052
|
+
scope: string,
|
|
1053
|
+
status: Exclude<TeamSnapshotStatus, 'ok'>,
|
|
1054
|
+
): TeamSnapshot {
|
|
1055
|
+
return {
|
|
1056
|
+
status,
|
|
1057
|
+
mode,
|
|
1058
|
+
readOnly: true,
|
|
1059
|
+
scope,
|
|
1060
|
+
agents: [],
|
|
1061
|
+
activeCount: 0,
|
|
1062
|
+
recentCount: 0,
|
|
1063
|
+
historicalCount: 0,
|
|
1064
|
+
totalAgents: 0,
|
|
1065
|
+
recentWindowDays: 7,
|
|
1066
|
+
locks: [],
|
|
1067
|
+
tasks: [],
|
|
1068
|
+
availableTasks: 0,
|
|
1069
|
+
sessions: 0,
|
|
1070
|
+
roles: [],
|
|
1071
|
+
roleOccupancy: [],
|
|
1072
|
+
handoffs: [],
|
|
1073
|
+
openTasks: 0,
|
|
1074
|
+
openHandoffs: 0,
|
|
1075
|
+
totalUnread: 0,
|
|
1076
|
+
activeSessions: 0,
|
|
1077
|
+
error: status === 'error' ? 'Coordination state unavailable' : 'Coordination state is incomplete',
|
|
1078
|
+
errorCode: status === 'error' ? 'COORDINATION_STATE_UNAVAILABLE' : 'COORDINATION_STATE_DEGRADED',
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
function logTeamSnapshotFailure(scope: string, status: Exclude<TeamSnapshotStatus, 'ok'>): void {
|
|
1083
|
+
const safeScope = scope === 'global' ? 'global' : 'project';
|
|
1084
|
+
console.error('[dashboard] coordination snapshot unavailable', { status, scope: safeScope });
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
async function buildTeamSnapshot(dataDir: string, projectId: string, scope: string, mode: DashboardState['mode']): Promise<TeamSnapshot> {
|
|
1031
1088
|
try {
|
|
1032
1089
|
const { initTeamStore } = await import('../team/team-store.js');
|
|
1033
1090
|
const teamStore = await initTeamStore(dataDir);
|
|
@@ -1053,8 +1110,9 @@ async function buildTeamSnapshot(dataDir: string, projectId: string, scope: stri
|
|
|
1053
1110
|
const roleOccupancy = effectiveProjectId ? teamStore.getRoleOccupancy(effectiveProjectId) : [];
|
|
1054
1111
|
const handoffs = effectiveProjectId ? teamStore.listHandoffs(effectiveProjectId) : [];
|
|
1055
1112
|
return {
|
|
1113
|
+
status: 'ok',
|
|
1056
1114
|
mode,
|
|
1057
|
-
readOnly:
|
|
1115
|
+
readOnly: true,
|
|
1058
1116
|
scope,
|
|
1059
1117
|
agents: withTier,
|
|
1060
1118
|
activeCount,
|
|
@@ -1075,28 +1133,8 @@ async function buildTeamSnapshot(dataDir: string, projectId: string, scope: stri
|
|
|
1075
1133
|
activeSessions: activeCount,
|
|
1076
1134
|
};
|
|
1077
1135
|
} catch {
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
readOnly: mode === 'standalone',
|
|
1081
|
-
scope,
|
|
1082
|
-
agents: [],
|
|
1083
|
-
activeCount: 0,
|
|
1084
|
-
recentCount: 0,
|
|
1085
|
-
historicalCount: 0,
|
|
1086
|
-
totalAgents: 0,
|
|
1087
|
-
recentWindowDays: 7,
|
|
1088
|
-
locks: [],
|
|
1089
|
-
tasks: [],
|
|
1090
|
-
availableTasks: 0,
|
|
1091
|
-
sessions: 0,
|
|
1092
|
-
roles: [],
|
|
1093
|
-
roleOccupancy: [],
|
|
1094
|
-
handoffs: [],
|
|
1095
|
-
openTasks: 0,
|
|
1096
|
-
openHandoffs: 0,
|
|
1097
|
-
totalUnread: 0,
|
|
1098
|
-
activeSessions: 0,
|
|
1099
|
-
};
|
|
1136
|
+
logTeamSnapshotFailure(scope, 'error');
|
|
1137
|
+
return emptyTeamSnapshot(mode, scope, 'error');
|
|
1100
1138
|
}
|
|
1101
1139
|
}
|
|
1102
1140
|
|
|
@@ -1125,7 +1163,6 @@ export async function startDashboard(
|
|
|
1125
1163
|
projectId: string,
|
|
1126
1164
|
projectName: string,
|
|
1127
1165
|
autoOpen = true,
|
|
1128
|
-
teamInstances?: TeamInstances,
|
|
1129
1166
|
projectRoot: string | null = null,
|
|
1130
1167
|
projectResolved = true,
|
|
1131
1168
|
): Promise<void> {
|
|
@@ -1136,8 +1173,7 @@ export async function startDashboard(
|
|
|
1136
1173
|
const baseDir = getBaseDataDir();
|
|
1137
1174
|
|
|
1138
1175
|
// Mutable state — can be updated via /api/set-current-project
|
|
1139
|
-
const
|
|
1140
|
-
const state: DashboardState = { projectId, projectName, dataDir, projectRoot, projectResolved, mode: isControlPlane ? 'control-plane' : 'standalone', port };
|
|
1176
|
+
const state: DashboardState = { projectId, projectName, dataDir, projectRoot, projectResolved, mode: 'standalone', port };
|
|
1141
1177
|
|
|
1142
1178
|
const server = createServer(async (req, res) => {
|
|
1143
1179
|
const url = req.url || '/';
|
|
@@ -1164,56 +1200,20 @@ export async function startDashboard(
|
|
|
1164
1200
|
return;
|
|
1165
1201
|
}
|
|
1166
1202
|
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
const scope = parsedUrl.searchParams.get('scope') || 'project';
|
|
1171
|
-
sendJson(res, await buildTeamSnapshot(state.dataDir, state.projectId, scope, state.mode));
|
|
1172
|
-
return;
|
|
1173
|
-
}
|
|
1174
|
-
try {
|
|
1175
|
-
teamInstances.fileLocks.cleanExpired();
|
|
1176
|
-
const agents = teamInstances.registry.listAgents();
|
|
1177
|
-
const locks = teamInstances.fileLocks.listLocks();
|
|
1178
|
-
const tasks = teamInstances.taskManager.list();
|
|
1179
|
-
const available = teamInstances.taskManager.getAvailable();
|
|
1180
|
-
|
|
1181
|
-
// Role occupancy and handoffs from TeamStore (if available)
|
|
1182
|
-
let roles: any[] = [];
|
|
1183
|
-
let roleOccupancy: any[] = [];
|
|
1184
|
-
let handoffs: any[] = [];
|
|
1185
|
-
try {
|
|
1186
|
-
const { getTeamStore, isTeamStoreInitialized } = await import('../team/team-store.js');
|
|
1187
|
-
if (isTeamStoreInitialized()) {
|
|
1188
|
-
const teamStore = getTeamStore();
|
|
1189
|
-
const projectId = state.projectId;
|
|
1190
|
-
roles = teamStore.listRoles(projectId);
|
|
1191
|
-
roleOccupancy = teamStore.getRoleOccupancy(projectId);
|
|
1192
|
-
handoffs = teamStore.listHandoffs(projectId);
|
|
1193
|
-
}
|
|
1194
|
-
} catch { /* team store not available in standalone mode */ }
|
|
1195
|
-
|
|
1203
|
+
const parsedUrl = new URL(url, `http://127.0.0.1:${port}`);
|
|
1204
|
+
if (parsedUrl.pathname === '/api/team') {
|
|
1205
|
+
if (req.method !== 'GET') {
|
|
1196
1206
|
sendJson(res, {
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
tasks,
|
|
1204
|
-
availableTasks: available.length,
|
|
1205
|
-
roles,
|
|
1206
|
-
roleOccupancy,
|
|
1207
|
-
handoffs,
|
|
1208
|
-
// Resume data for "Continue this project" area
|
|
1209
|
-
openTasks: tasks.filter((t: any) => t.status === 'pending' || t.status === 'in_progress').length,
|
|
1210
|
-
openHandoffs: handoffs.filter((h: any) => h.handoff_status === 'open' || h.handoffStatus === 'open').length,
|
|
1211
|
-
totalUnread: agents.reduce((sum: number, a: any) => sum + teamInstances!.messageBus.getUnreadCount(a.id), 0),
|
|
1212
|
-
activeSessions: agents.filter((a: any) => a.status === 'active').length,
|
|
1213
|
-
});
|
|
1214
|
-
} catch {
|
|
1215
|
-
sendJson(res, { agents: [], activeCount: 0, locks: [], tasks: [], availableTasks: 0, roles: [], roleOccupancy: [], handoffs: [] });
|
|
1207
|
+
status: 'error',
|
|
1208
|
+
readOnly: true,
|
|
1209
|
+
error: 'Coordination status is read-only',
|
|
1210
|
+
errorCode: 'COORDINATION_STATUS_READ_ONLY',
|
|
1211
|
+
}, 405);
|
|
1212
|
+
return;
|
|
1216
1213
|
}
|
|
1214
|
+
const scope = parsedUrl.searchParams.get('scope') || 'project';
|
|
1215
|
+
const snapshot = await buildTeamSnapshot(state.dataDir, state.projectId, scope, state.mode);
|
|
1216
|
+
sendJson(res, snapshot, snapshot.status === 'error' ? 503 : 200);
|
|
1217
1217
|
return;
|
|
1218
1218
|
}
|
|
1219
1219
|
|
|
@@ -1237,13 +1237,12 @@ export async function startDashboard(
|
|
|
1237
1237
|
server.listen(port, '127.0.0.1', () => {
|
|
1238
1238
|
const url = `http://127.0.0.1:${port}`;
|
|
1239
1239
|
const resolvedLabel = projectResolved ? 'resolved' : 'unresolved';
|
|
1240
|
-
const modeLabel =
|
|
1240
|
+
const modeLabel = 'Standalone';
|
|
1241
1241
|
console.error(` Memorix Dashboard [${modeLabel}]`);
|
|
1242
1242
|
console.error(` ───────────────────────`);
|
|
1243
1243
|
console.error(` Mode: ${modeLabel}`);
|
|
1244
1244
|
console.error(` Project: ${projectName} (${projectId}) [${resolvedLabel}]`);
|
|
1245
1245
|
console.error(` Local: ${url}`);
|
|
1246
|
-
if (isControlPlane) console.error(` MCP: ${url}/mcp`);
|
|
1247
1246
|
console.error(` Data dir: ${dataDir}`);
|
|
1248
1247
|
console.error(`\n Press Ctrl+C to stop\n`);
|
|
1249
1248
|
if (autoOpen) openBrowser(url);
|
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
</svg>
|
|
136
136
|
<span class="nav-label">Sessions</span>
|
|
137
137
|
</button>
|
|
138
|
-
<button class="nav-btn" data-page="team" title="Coordination" aria-label="Coordination">
|
|
138
|
+
<button class="nav-btn" data-page="team" title="Coordination Status" aria-label="Coordination Status">
|
|
139
139
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
140
140
|
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
|
141
141
|
<circle cx="9" cy="7" r="4"/>
|
package/src/server.ts
CHANGED
|
@@ -198,7 +198,7 @@ function createDeterministicInstanceId(projectId: string, agentType: string, age
|
|
|
198
198
|
* Create and configure the Memorix MCP Server.
|
|
199
199
|
*/
|
|
200
200
|
/** Optional shared TeamStore — passed by serve-http so all sessions share state */
|
|
201
|
-
export interface
|
|
201
|
+
export interface SharedTeamStore {
|
|
202
202
|
teamStore: import('./team/team-store.js').TeamStore;
|
|
203
203
|
}
|
|
204
204
|
|
|
@@ -245,7 +245,7 @@ export function shouldAwaitProjectRuntime(toolName: string): boolean {
|
|
|
245
245
|
export async function createMemorixServer(
|
|
246
246
|
cwd?: string,
|
|
247
247
|
existingServer?: McpServer,
|
|
248
|
-
sharedTeam?:
|
|
248
|
+
sharedTeam?: SharedTeamStore,
|
|
249
249
|
options: CreateMemorixServerOptions = {},
|
|
250
250
|
): Promise<{
|
|
251
251
|
server: McpServer;
|
|
@@ -4420,7 +4420,7 @@ export async function createMemorixServer(
|
|
|
4420
4420
|
console.error(`[memorix] Dashboard staticDir: ${staticDir}`);
|
|
4421
4421
|
|
|
4422
4422
|
// Start in background (non-blocking), disable auto-open (we'll open it ourselves)
|
|
4423
|
-
startDashboard(projectDir, portNum, staticDir, project.id, project.name, false,
|
|
4423
|
+
startDashboard(projectDir, portNum, staticDir, project.id, project.name, false, project.rootPath, projectResolved)
|
|
4424
4424
|
.then(() => { dashboardRunning = true; })
|
|
4425
4425
|
.catch((err) => { console.error('[memorix] Dashboard error:', err); dashboardRunning = false; });
|
|
4426
4426
|
|
|
@@ -4786,7 +4786,7 @@ export async function createMemorixServer(
|
|
|
4786
4786
|
server.registerTool(
|
|
4787
4787
|
'team_task',
|
|
4788
4788
|
{
|
|
4789
|
-
title: 'Task
|
|
4789
|
+
title: 'Task Status',
|
|
4790
4790
|
description:
|
|
4791
4791
|
'Create, claim, complete, or list tasks in the team task board. Supports dependencies. ' +
|
|
4792
4792
|
'Action "create": create a task. Action "claim": assign to yourself (atomic, race-safe). ' +
|