@kl-c/matrixos 0.3.0 → 0.3.2

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/dist/cli/index.js CHANGED
@@ -2163,7 +2163,7 @@ var package_default;
2163
2163
  var init_package = __esm(() => {
2164
2164
  package_default = {
2165
2165
  name: "@kl-c/matrixos",
2166
- version: "0.3.0",
2166
+ version: "0.3.2",
2167
2167
  description: "MaTrixOS \u2014 Agentic OS for OpenCode. Personalizable, communicating, self-improving, resilient.",
2168
2168
  main: "./dist/index.js",
2169
2169
  types: "dist/index.d.ts",
@@ -186568,6 +186568,36 @@ var AGENT_ROLES = {
186568
186568
  Neo: "Executor",
186569
186569
  Tank: "Deep Work"
186570
186570
  };
186571
+ var DEFAULT_MODEL = "opencode/deepseek-v4-flash-free";
186572
+ var AVAILABLE_MODELS = [
186573
+ "opencode/deepseek-v4-flash-free",
186574
+ "opencode/laguna-s-2.1-free",
186575
+ "opencode/mimo-v2.5-free",
186576
+ "opencode/nemotron-3-ultra-free",
186577
+ "opencode/north-mini-code-free",
186578
+ "opencode/big-pickle",
186579
+ "github-copilot/claude-opus-4.7",
186580
+ "anthropic/claude-opus-4",
186581
+ "openai/gpt-5"
186582
+ ];
186583
+ function readAgentModels() {
186584
+ try {
186585
+ const cfgPath = path24.resolve(os12.homedir(), ".matrixos", "matrixos.json");
186586
+ if (fs21.existsSync(cfgPath)) {
186587
+ const raw = fs21.readFileSync(cfgPath, "utf-8").replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
186588
+ const parsed = JSON.parse(raw);
186589
+ const result = {};
186590
+ if (parsed.agents) {
186591
+ for (const [name2, cfg] of Object.entries(parsed.agents)) {
186592
+ if (cfg?.model)
186593
+ result[name2] = cfg.model;
186594
+ }
186595
+ }
186596
+ return result;
186597
+ }
186598
+ } catch {}
186599
+ return {};
186600
+ }
186571
186601
  var OPENCODE_DB_PATH = path24.resolve(os12.homedir(), ".local", "share", "opencode", "opencode.db");
186572
186602
  var THIRTY_MIN_MS = 30 * 60 * 1000;
186573
186603
  var FIVE_MIN_MS = 5 * 60 * 1000;
@@ -186685,11 +186715,13 @@ function createDataProvider(config5) {
186685
186715
  WHERE agent IS NOT NULL AND agent != ''
186686
186716
  GROUP BY agent`).all();
186687
186717
  if (rows.length > 0) {
186718
+ const cfgAgents2 = readAgentModels();
186688
186719
  const result = [];
186689
186720
  for (const row of rows) {
186690
186721
  const name2 = row.agent;
186691
186722
  const onlineRows = db.query("SELECT COUNT(*) as count FROM session WHERE agent = ? AND time_updated > ?").all(name2, cutoff);
186692
186723
  const status2 = onlineRows.length > 0 && onlineRows[0].count > 0 ? "online" : "idle";
186724
+ const model = cfgAgents2[name2];
186693
186725
  result.push({
186694
186726
  name: name2,
186695
186727
  role: AGENT_ROLES[name2] ?? "Agent",
@@ -186697,7 +186729,9 @@ function createDataProvider(config5) {
186697
186729
  sessions: row.sessions,
186698
186730
  uptime: 0,
186699
186731
  cpuPercent: 0,
186700
- memoryPercent: 0
186732
+ memoryPercent: 0,
186733
+ model: model ?? DEFAULT_MODEL,
186734
+ modelInherited: !model
186701
186735
  });
186702
186736
  }
186703
186737
  db.close();
@@ -186707,15 +186741,45 @@ function createDataProvider(config5) {
186707
186741
  db.close();
186708
186742
  }
186709
186743
  }
186710
- return AGENT_NAMES.map((name2) => ({
186711
- name: name2,
186712
- role: AGENT_ROLES[name2] ?? "Agent",
186713
- status: "idle",
186714
- sessions: 0,
186715
- uptime: 0,
186716
- cpuPercent: 0,
186717
- memoryPercent: 0
186718
- }));
186744
+ const cfgAgents = readAgentModels();
186745
+ return AGENT_NAMES.map((name2) => {
186746
+ const model = cfgAgents[name2];
186747
+ return {
186748
+ name: name2,
186749
+ role: AGENT_ROLES[name2] ?? "Agent",
186750
+ status: "idle",
186751
+ sessions: 0,
186752
+ uptime: 0,
186753
+ cpuPercent: 0,
186754
+ memoryPercent: 0,
186755
+ model: model ?? DEFAULT_MODEL,
186756
+ modelInherited: !model
186757
+ };
186758
+ });
186759
+ },
186760
+ getModels() {
186761
+ return AVAILABLE_MODELS;
186762
+ },
186763
+ updateAgentModel(agentName, model) {
186764
+ try {
186765
+ const cfgPath = path24.resolve(matrixosHome, "matrixos.json");
186766
+ let cfg = {};
186767
+ if (fs21.existsSync(cfgPath)) {
186768
+ try {
186769
+ const raw = fs21.readFileSync(cfgPath, "utf-8").replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
186770
+ const parsed = JSON.parse(raw);
186771
+ if (parsed && typeof parsed === "object")
186772
+ cfg = parsed;
186773
+ } catch {}
186774
+ }
186775
+ const agents = cfg.agents ?? {};
186776
+ agents[agentName] = { ...agents[agentName] ?? {}, model };
186777
+ cfg.agents = agents;
186778
+ fs21.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2), "utf-8");
186779
+ return { ok: true };
186780
+ } catch (error51) {
186781
+ return { ok: false, error: error51 instanceof Error ? error51.message : "Write failed" };
186782
+ }
186719
186783
  },
186720
186784
  async getSessions(limit = 20) {
186721
186785
  const db = getDb();
@@ -186855,15 +186919,7 @@ function createDataProvider(config5) {
186855
186919
  async getIncidents(limit = 50) {
186856
186920
  const lines = readJsonl("anti-loop.jsonl");
186857
186921
  if (lines.length === 0) {
186858
- return Array.from({ length: 10 }, () => ({
186859
- timestamp: Date.now() - randomBetween(0, 86400000),
186860
- trigger: randomChoice(["heartbeat_loss", "memory_exceeded", "cpu_exceeded", "doom_loop", "stuck"]),
186861
- fromLevel: randomChoice(["nudge", "fallback", "restart"]),
186862
- toLevel: randomChoice(["nudge", "fallback", "restart", "notify", "safe"]),
186863
- actionTaken: randomChoice(["nudge", "fallback", "restart", "notify"]),
186864
- outcome: randomChoice(["success", "success", "success", "failed"]),
186865
- detail: "Mock incident data"
186866
- })).slice(0, limit);
186922
+ return [];
186867
186923
  }
186868
186924
  return lines.slice(0, limit).map((l2) => {
186869
186925
  try {
@@ -186890,13 +186946,18 @@ function createDataProvider(config5) {
186890
186946
  const mem = process.memoryUsage();
186891
186947
  const memPercent = Math.round(mem.rss / totalMem * 100);
186892
186948
  const cpuPercent = Math.round(os12.loadavg()[0] * 100);
186949
+ let diskPercent = 0;
186950
+ try {
186951
+ const stat = fs21.statfsSync("/");
186952
+ diskPercent = Math.round((stat.blocks - stat.bavail) / stat.blocks * 100);
186953
+ } catch {}
186893
186954
  const snapshots = [];
186894
186955
  for (let i3 = 0;i3 < 60; i3++) {
186895
186956
  snapshots.push({
186896
186957
  timestamp: Date.now() - (59 - i3) * 60000,
186897
186958
  cpuPercent,
186898
186959
  memoryPercent: memPercent,
186899
- diskPercent: 0,
186960
+ diskPercent,
186900
186961
  sessionsActive: activeSessions
186901
186962
  });
186902
186963
  }
@@ -186922,34 +186983,6 @@ function createDataProvider(config5) {
186922
186983
  } catch {}
186923
186984
  }
186924
186985
  }
186925
- if (entries.length < 20) {
186926
- const mockMsgs = [
186927
- ["Session started", "info"],
186928
- ["Tool executed: read_file", "info"],
186929
- ["Token count: 12,847", "debug"],
186930
- ["Model switch: deepseek-v4-flash-free", "info"],
186931
- ["Cost threshold warning: 78%", "warn"],
186932
- ["Heartbeat received from Morpheus", "debug"],
186933
- ["Session completed (4m 23s)", "info"],
186934
- ["Memory compaction triggered", "info"],
186935
- ["Config change detected", "info"],
186936
- ["Anti-loop triggered: Boulder retry cycle", "error"],
186937
- ["Cache hit rate: 89%", "info"],
186938
- ["Background task completed", "info"],
186939
- ["Gateway reconnected", "info"],
186940
- ["API rate limit: 45/60", "warn"],
186941
- ["Watchdog cycle #1247", "debug"],
186942
- ["Session ses_a1b2c3d4 failed", "error"]
186943
- ];
186944
- for (const [msg, level] of mockMsgs.slice(0, 30 - entries.length)) {
186945
- entries.push({
186946
- timestamp: new Date(Date.now() - randomBetween(0, 3600000)).toISOString(),
186947
- level,
186948
- source: randomChoice(["system", "gateway", "agent", "watchdog"]),
186949
- message: msg
186950
- });
186951
- }
186952
- }
186953
186986
  return entries.sort((a2, b2) => b2.timestamp.localeCompare(a2.timestamp)).slice(0, 100);
186954
186987
  },
186955
186988
  getConfig() {
@@ -187175,8 +187208,19 @@ function createDashboardServer(dataProvider, config5) {
187175
187208
  switch (path25) {
187176
187209
  case "/api/health":
187177
187210
  return Response.json(await dataProvider.getHealth(), { headers: jsonHeaders });
187178
- case "/api/agents":
187211
+ case "/api/agents": {
187212
+ if (req.method === "PUT") {
187213
+ const body = await req.json();
187214
+ if (!body.agentName || !body.model) {
187215
+ return Response.json({ ok: false, error: "agentName and model required" }, { status: 400, headers: jsonHeaders });
187216
+ }
187217
+ const result = dataProvider.updateAgentModel(body.agentName, body.model);
187218
+ return Response.json(result, { headers: jsonHeaders });
187219
+ }
187179
187220
  return Response.json(await dataProvider.getAgents(), { headers: jsonHeaders });
187221
+ }
187222
+ case "/api/models":
187223
+ return Response.json(dataProvider.getModels(), { headers: jsonHeaders });
187180
187224
  case "/api/sessions": {
187181
187225
  const limit = parseInt(url3.searchParams.get("limit") ?? "20", 10);
187182
187226
  return Response.json(await dataProvider.getSessions(limit), { headers: jsonHeaders });
@@ -2163,7 +2163,7 @@ var package_default;
2163
2163
  var init_package = __esm(() => {
2164
2164
  package_default = {
2165
2165
  name: "@kl-c/matrixos",
2166
- version: "0.3.0",
2166
+ version: "0.3.2",
2167
2167
  description: "MaTrixOS \u2014 Agentic OS for OpenCode. Personalizable, communicating, self-improving, resilient.",
2168
2168
  main: "./dist/index.js",
2169
2169
  types: "dist/index.d.ts",
@@ -186623,6 +186623,36 @@ var AGENT_ROLES = {
186623
186623
  Neo: "Executor",
186624
186624
  Tank: "Deep Work"
186625
186625
  };
186626
+ var DEFAULT_MODEL = "opencode/deepseek-v4-flash-free";
186627
+ var AVAILABLE_MODELS = [
186628
+ "opencode/deepseek-v4-flash-free",
186629
+ "opencode/laguna-s-2.1-free",
186630
+ "opencode/mimo-v2.5-free",
186631
+ "opencode/nemotron-3-ultra-free",
186632
+ "opencode/north-mini-code-free",
186633
+ "opencode/big-pickle",
186634
+ "github-copilot/claude-opus-4.7",
186635
+ "anthropic/claude-opus-4",
186636
+ "openai/gpt-5"
186637
+ ];
186638
+ function readAgentModels() {
186639
+ try {
186640
+ const cfgPath = path24.resolve(os12.homedir(), ".matrixos", "matrixos.json");
186641
+ if (fs21.existsSync(cfgPath)) {
186642
+ const raw = fs21.readFileSync(cfgPath, "utf-8").replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
186643
+ const parsed = JSON.parse(raw);
186644
+ const result = {};
186645
+ if (parsed.agents) {
186646
+ for (const [name2, cfg] of Object.entries(parsed.agents)) {
186647
+ if (cfg?.model)
186648
+ result[name2] = cfg.model;
186649
+ }
186650
+ }
186651
+ return result;
186652
+ }
186653
+ } catch {}
186654
+ return {};
186655
+ }
186626
186656
  var OPENCODE_DB_PATH = path24.resolve(os12.homedir(), ".local", "share", "opencode", "opencode.db");
186627
186657
  var THIRTY_MIN_MS = 30 * 60 * 1000;
186628
186658
  var FIVE_MIN_MS = 5 * 60 * 1000;
@@ -186740,11 +186770,13 @@ function createDataProvider(config5) {
186740
186770
  WHERE agent IS NOT NULL AND agent != ''
186741
186771
  GROUP BY agent`).all();
186742
186772
  if (rows.length > 0) {
186773
+ const cfgAgents2 = readAgentModels();
186743
186774
  const result = [];
186744
186775
  for (const row of rows) {
186745
186776
  const name2 = row.agent;
186746
186777
  const onlineRows = db.query("SELECT COUNT(*) as count FROM session WHERE agent = ? AND time_updated > ?").all(name2, cutoff);
186747
186778
  const status2 = onlineRows.length > 0 && onlineRows[0].count > 0 ? "online" : "idle";
186779
+ const model = cfgAgents2[name2];
186748
186780
  result.push({
186749
186781
  name: name2,
186750
186782
  role: AGENT_ROLES[name2] ?? "Agent",
@@ -186752,7 +186784,9 @@ function createDataProvider(config5) {
186752
186784
  sessions: row.sessions,
186753
186785
  uptime: 0,
186754
186786
  cpuPercent: 0,
186755
- memoryPercent: 0
186787
+ memoryPercent: 0,
186788
+ model: model ?? DEFAULT_MODEL,
186789
+ modelInherited: !model
186756
186790
  });
186757
186791
  }
186758
186792
  db.close();
@@ -186762,15 +186796,45 @@ function createDataProvider(config5) {
186762
186796
  db.close();
186763
186797
  }
186764
186798
  }
186765
- return AGENT_NAMES.map((name2) => ({
186766
- name: name2,
186767
- role: AGENT_ROLES[name2] ?? "Agent",
186768
- status: "idle",
186769
- sessions: 0,
186770
- uptime: 0,
186771
- cpuPercent: 0,
186772
- memoryPercent: 0
186773
- }));
186799
+ const cfgAgents = readAgentModels();
186800
+ return AGENT_NAMES.map((name2) => {
186801
+ const model = cfgAgents[name2];
186802
+ return {
186803
+ name: name2,
186804
+ role: AGENT_ROLES[name2] ?? "Agent",
186805
+ status: "idle",
186806
+ sessions: 0,
186807
+ uptime: 0,
186808
+ cpuPercent: 0,
186809
+ memoryPercent: 0,
186810
+ model: model ?? DEFAULT_MODEL,
186811
+ modelInherited: !model
186812
+ };
186813
+ });
186814
+ },
186815
+ getModels() {
186816
+ return AVAILABLE_MODELS;
186817
+ },
186818
+ updateAgentModel(agentName, model) {
186819
+ try {
186820
+ const cfgPath = path24.resolve(matrixosHome, "matrixos.json");
186821
+ let cfg = {};
186822
+ if (fs21.existsSync(cfgPath)) {
186823
+ try {
186824
+ const raw = fs21.readFileSync(cfgPath, "utf-8").replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
186825
+ const parsed = JSON.parse(raw);
186826
+ if (parsed && typeof parsed === "object")
186827
+ cfg = parsed;
186828
+ } catch {}
186829
+ }
186830
+ const agents = cfg.agents ?? {};
186831
+ agents[agentName] = { ...agents[agentName] ?? {}, model };
186832
+ cfg.agents = agents;
186833
+ fs21.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2), "utf-8");
186834
+ return { ok: true };
186835
+ } catch (error51) {
186836
+ return { ok: false, error: error51 instanceof Error ? error51.message : "Write failed" };
186837
+ }
186774
186838
  },
186775
186839
  async getSessions(limit = 20) {
186776
186840
  const db = getDb();
@@ -186910,15 +186974,7 @@ function createDataProvider(config5) {
186910
186974
  async getIncidents(limit = 50) {
186911
186975
  const lines = readJsonl("anti-loop.jsonl");
186912
186976
  if (lines.length === 0) {
186913
- return Array.from({ length: 10 }, () => ({
186914
- timestamp: Date.now() - randomBetween(0, 86400000),
186915
- trigger: randomChoice(["heartbeat_loss", "memory_exceeded", "cpu_exceeded", "doom_loop", "stuck"]),
186916
- fromLevel: randomChoice(["nudge", "fallback", "restart"]),
186917
- toLevel: randomChoice(["nudge", "fallback", "restart", "notify", "safe"]),
186918
- actionTaken: randomChoice(["nudge", "fallback", "restart", "notify"]),
186919
- outcome: randomChoice(["success", "success", "success", "failed"]),
186920
- detail: "Mock incident data"
186921
- })).slice(0, limit);
186977
+ return [];
186922
186978
  }
186923
186979
  return lines.slice(0, limit).map((l2) => {
186924
186980
  try {
@@ -186945,13 +187001,18 @@ function createDataProvider(config5) {
186945
187001
  const mem = process.memoryUsage();
186946
187002
  const memPercent = Math.round(mem.rss / totalMem * 100);
186947
187003
  const cpuPercent = Math.round(os12.loadavg()[0] * 100);
187004
+ let diskPercent = 0;
187005
+ try {
187006
+ const stat = fs21.statfsSync("/");
187007
+ diskPercent = Math.round((stat.blocks - stat.bavail) / stat.blocks * 100);
187008
+ } catch {}
186948
187009
  const snapshots = [];
186949
187010
  for (let i3 = 0;i3 < 60; i3++) {
186950
187011
  snapshots.push({
186951
187012
  timestamp: Date.now() - (59 - i3) * 60000,
186952
187013
  cpuPercent,
186953
187014
  memoryPercent: memPercent,
186954
- diskPercent: 0,
187015
+ diskPercent,
186955
187016
  sessionsActive: activeSessions
186956
187017
  });
186957
187018
  }
@@ -186977,34 +187038,6 @@ function createDataProvider(config5) {
186977
187038
  } catch {}
186978
187039
  }
186979
187040
  }
186980
- if (entries.length < 20) {
186981
- const mockMsgs = [
186982
- ["Session started", "info"],
186983
- ["Tool executed: read_file", "info"],
186984
- ["Token count: 12,847", "debug"],
186985
- ["Model switch: deepseek-v4-flash-free", "info"],
186986
- ["Cost threshold warning: 78%", "warn"],
186987
- ["Heartbeat received from Morpheus", "debug"],
186988
- ["Session completed (4m 23s)", "info"],
186989
- ["Memory compaction triggered", "info"],
186990
- ["Config change detected", "info"],
186991
- ["Anti-loop triggered: Boulder retry cycle", "error"],
186992
- ["Cache hit rate: 89%", "info"],
186993
- ["Background task completed", "info"],
186994
- ["Gateway reconnected", "info"],
186995
- ["API rate limit: 45/60", "warn"],
186996
- ["Watchdog cycle #1247", "debug"],
186997
- ["Session ses_a1b2c3d4 failed", "error"]
186998
- ];
186999
- for (const [msg, level] of mockMsgs.slice(0, 30 - entries.length)) {
187000
- entries.push({
187001
- timestamp: new Date(Date.now() - randomBetween(0, 3600000)).toISOString(),
187002
- level,
187003
- source: randomChoice(["system", "gateway", "agent", "watchdog"]),
187004
- message: msg
187005
- });
187006
- }
187007
- }
187008
187041
  return entries.sort((a2, b2) => b2.timestamp.localeCompare(a2.timestamp)).slice(0, 100);
187009
187042
  },
187010
187043
  getConfig() {
@@ -187230,8 +187263,19 @@ function createDashboardServer(dataProvider, config5) {
187230
187263
  switch (path25) {
187231
187264
  case "/api/health":
187232
187265
  return Response.json(await dataProvider.getHealth(), { headers: jsonHeaders });
187233
- case "/api/agents":
187266
+ case "/api/agents": {
187267
+ if (req.method === "PUT") {
187268
+ const body = await req.json();
187269
+ if (!body.agentName || !body.model) {
187270
+ return Response.json({ ok: false, error: "agentName and model required" }, { status: 400, headers: jsonHeaders });
187271
+ }
187272
+ const result = dataProvider.updateAgentModel(body.agentName, body.model);
187273
+ return Response.json(result, { headers: jsonHeaders });
187274
+ }
187234
187275
  return Response.json(await dataProvider.getAgents(), { headers: jsonHeaders });
187276
+ }
187277
+ case "/api/models":
187278
+ return Response.json(dataProvider.getModels(), { headers: jsonHeaders });
187235
187279
  case "/api/sessions": {
187236
187280
  const limit = parseInt(url3.searchParams.get("limit") ?? "20", 10);
187237
187281
  return Response.json(await dataProvider.getSessions(limit), { headers: jsonHeaders });
@@ -5,6 +5,11 @@ export interface DataProviderConfig {
5
5
  export interface DataProvider {
6
6
  getHealth(): Promise<HealthResponse>;
7
7
  getAgents(): Promise<AgentStatus[]>;
8
+ getModels(): string[];
9
+ updateAgentModel(agentName: string, model: string): {
10
+ ok: boolean;
11
+ error?: string;
12
+ };
8
13
  getSessions(limit?: number): Promise<SessionSummary[]>;
9
14
  getCost(): Promise<CostSummary>;
10
15
  getKanbanTasks(): Promise<KanbanBoard>;
@@ -116,44 +116,39 @@ async function loadHealth(){
116
116
  const kpi=document.getElementById('healthKpis'),gauges=document.getElementById('gaugeGrid'),hb=document.getElementById('heartbeatGrid'),inc=document.getElementById('incidentList')
117
117
  showSkeleton(kpi,'kpi')
118
118
  try{
119
- const[health,metrics,incidents]=await Promise.all([API.health(),API.metrics(),API.incidents(8)])
120
- const cpu=metrics.length?metrics[metrics.length-1].cpuPercent:45
121
- const mem=health.memoryUsage?health.memoryUsage.percent||50:50
122
- const disk=65
119
+ const[health,metrics,incidents,agents]=await Promise.all([API.health(),API.metrics(),API.incidents(8),API.agents()])
120
+ const cpu=metrics.length?metrics[metrics.length-1].cpuPercent:0
121
+ const mem=health.memoryUsage?health.memoryUsage.percent||0:0
122
+ const disk=metrics.length?metrics[metrics.length-1].diskPercent||0:0
123
123
  const cpuColor=cpu>80?'var(--danger)':cpu>60?'var(--warning)':'var(--success)'
124
124
  const memColor=mem>80?'var(--danger)':mem>60?'var(--warning)':'var(--success)'
125
125
  const diskColor=disk>80?'var(--danger)':disk>60?'var(--warning)':'var(--info)'
126
126
 
127
127
  kpi.innerHTML=`
128
- <div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">System Status</span><div class="kpi-icon green"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M4 8l3 3 5-6"/></svg></div></div><div class="kpi-value" style="color:var(--success)">Healthy</div><div class="kpi-trend flat"><span class="live-dot" style="margin-right:4px"></span>All systems operational</div></div>
129
- <div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">Uptime</span><div class="kpi-icon cyan"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="8" r="6"/><path d="M8 4.5v3.5l2.5 1.5"/></svg></div></div><div class="kpi-value">99.97%</div><div class="kpi-trend up">Last 30d: ${rand(5,20)}m downtime</div></div>
130
- <div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">Active Incidents</span><div class="kpi-icon red"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 2L1.5 13h13L8 2z"/><path d="M8 7v2.5M8 11.5v.5"/></svg></div></div><div class="kpi-value" style="color:var(--danger)">${incidents.length}</div><div class="kpi-trend down"><svg width="10" height="10" viewBox="0 0 10 10" fill="currentColor"><path d="M5 2L9 7H1z"/></svg>+${rand(0,3)} in last hour</div></div>
131
- <div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">Watchdog Cycles</span><div class="kpi-icon blue"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="8" r="6"/><path d="M8 5v3"/><circle cx="8" cy="11" r="0.5" fill="currentColor"/></svg></div></div><div class="kpi-value">${rand(800,2000)}</div><div class="kpi-trend flat">Last check: ${rand(1,10)}s ago</div></div>`
128
+ <div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">System Status</span><div class="kpi-icon green"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M4 8l3 3 5-6"/></svg></div></div><div class="kpi-value" style="color:var(--success)">Online</div><div class="kpi-trend flat"><span class="live-dot" style="margin-right:4px"></span>v${health.version}</div></div>
129
+ <div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">Uptime</span><div class="kpi-icon cyan"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="8" r="6"/><path d="M8 4.5v3.5l2.5 1.5"/></svg></div></div><div class="kpi-value">${fmtDur(Math.round(health.uptime))}</div><div class="kpi-trend up">Since last restart</div></div>
130
+ <div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">Active Incidents</span><div class="kpi-icon red"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 2L1.5 13h13L8 2z"/><path d="M8 7v2.5M8 11.5v.5"/></svg></div></div><div class="kpi-value" style="color:${incidents.length?'var(--danger)':'var(--success)'}">${incidents.length}</div><div class="kpi-trend ${incidents.length?'down':'flat'}">${incidents.length?'Needs attention':'All clear'}</div></div>
131
+ <div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">Active Sessions</span><div class="kpi-icon blue"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="8" r="6"/><path d="M8 5v3"/><circle cx="8" cy="11" r="0.5" fill="currentColor"/></svg></div></div><div class="kpi-value">${health.activeSessions}</div><div class="kpi-trend flat">${health.tasksQueued} queued</div></div>`
132
132
 
133
133
  // Resource gauges (SVG)
134
134
  function gauge(pct,color,label,sub){
135
135
  const r=16,c=2*Math.PI*r,offset=c-(c*pct/100)
136
136
  return `<div class="gauge-card"><div class="gauge-ring"><svg viewBox="0 0 40 40"><circle class="track" cx="20" cy="20" r="${r}"/><circle stroke="${color}" cx="20" cy="20" r="${r}" stroke-dasharray="${c}" stroke-dashoffset="${offset}" style="transition:stroke-dashoffset 500ms"/></svg><div class="gauge-val" style="color:${color}">${pct}%</div></div><div class="gauge-label">${label}</div><div class="gauge-sub">${sub}</div></div>`
137
137
  }
138
- const totalMem=(health.memoryUsage&&health.memoryUsage.total)||32
138
+ const totalMem=(health.memoryUsage&&health.memoryUsage.total)||0
139
139
  const usedMem=Math.round(totalMem*(mem||0)/100)
140
- gauges.innerHTML=gauge(Math.round(cpu),cpuColor,'CPU Usage','8 cores / 16 threads')+gauge(mem,memColor,'Memory',`${usedMem} / ${totalMem} GB`)+gauge(disk,diskColor,'Disk','270 / 500 GB')
140
+ gauges.innerHTML=gauge(Math.round(cpu),cpuColor,'CPU Usage','Load avg')+gauge(mem,memColor,'Memory',`${usedMem} / ${totalMem} MB`)+gauge(disk,diskColor,'Disk','/ filesystem')
141
141
 
142
- // Heartbeat Grid
143
- const agents=['Morpheus','Keymaker','Trinity','Oracle','Boulder']
142
+ // Heartbeat Grid — real agents
143
+ const agentNames=agents.slice(0,8).map(a=>a.name)
144
144
  const hbLegendEl=document.getElementById('hbLegend')
145
- hbLegendEl.innerHTML=`<span style="display:flex;align-items:center;gap:4px;font-size:var(--text-xs);color:var(--success)"><span style="width:6px;height:6px;border-radius:50%;background:var(--success)"></span>Healthy</span><span style="display:flex;align-items:center;gap:4px;font-size:var(--text-xs);color:var(--warning)"><span style="width:6px;height:6px;border-radius:50%;background:var(--warning)"></span>Degraded</span><span style="display:flex;align-items:center;gap:4px;font-size:var(--text-xs);color:var(--danger)"><span style="width:6px;height:6px;border-radius:50%;background:var(--danger)"></span>Missed</span>`
146
- hb.innerHTML=agents.map((name,i)=>{
147
- const initial=name[0]
148
- const cells=[]
149
- for(let t=0;t<8;t++){
150
- const r=Math.random()
151
- const cls=r>0.85?'fail':r>0.7&&i>2?'warn':'healthy'
152
- cells.push(`<div class="hb-cell ${cls}" title="${name} t-${(7-t)*10}s">${initial}</div>`)
153
- }
154
- return cells.join('')
145
+ hbLegendEl.innerHTML=`<span style="display:flex;align-items:center;gap:4px;font-size:var(--text-xs);color:var(--success)"><span style="width:6px;height:6px;border-radius:50%;background:var(--success)"></span>Online</span><span style="display:flex;align-items:center;gap:4px;font-size:var(--text-xs);color:var(--warning)"><span style="width:6px;height:6px;border-radius:50%;background:var(--warning)"></span>Idle</span><span style="display:flex;align-items:center;gap:4px;font-size:var(--text-xs);color:var(--danger)"><span style="width:6px;height:6px;border-radius:50%;background:var(--danger)"></span>Offline</span>`
146
+ hb.innerHTML=agentNames.map((name)=>{
147
+ const agent=agents.find(a=>a.name===name)
148
+ const cls=agent&&agent.status==='online'?'healthy':agent&&agent.status==='idle'?'warn':'fail'
149
+ return `<div class="hb-cell ${cls}" title="${name}">${name[0]}</div>`
155
150
  }).join('')
156
- hb.insertAdjacentHTML('afterend',`<div style="margin-top:var(--space-3);font-size:var(--text-xs);color:var(--text-tertiary)">Agents: ${agents.map(a=>a[0]+'='+a).join(', ')}</div>`)
151
+ hb.insertAdjacentHTML('afterend',`<div style="margin-top:var(--space-3);font-size:var(--text-xs);color:var(--text-tertiary)">Agents: ${agentNames.map(a=>a[0]+'='+a).join(', ')}</div>`)
157
152
 
158
153
  // Incidents
159
154
  if(!incidents.length){showEmpty(inc,'No incidents recorded');return}
@@ -6,6 +6,8 @@ export interface AgentStatus {
6
6
  uptime: number;
7
7
  cpuPercent: number;
8
8
  memoryPercent: number;
9
+ model?: string;
10
+ modelInherited?: boolean;
9
11
  }
10
12
  export interface SessionSummary {
11
13
  id: string;
package/dist/index.js CHANGED
@@ -368019,7 +368019,7 @@ function getCachedVersion(options = {}) {
368019
368019
  // package.json
368020
368020
  var package_default = {
368021
368021
  name: "@kl-c/matrixos",
368022
- version: "0.3.0",
368022
+ version: "0.3.2",
368023
368023
  description: "MaTrixOS \u2014 Agentic OS for OpenCode. Personalizable, communicating, self-improving, resilient.",
368024
368024
  main: "./dist/index.js",
368025
368025
  types: "dist/index.d.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kl-c/matrixos",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "MaTrixOS — Agentic OS for OpenCode. Personalizable, communicating, self-improving, resilient.",
5
5
  "main": "./dist/index.js",
6
6
  "types": "dist/index.d.ts",