agent-memory-brain 0.2.0__py3-none-any.whl
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.
- agent_memory_brain-0.2.0.dist-info/METADATA +98 -0
- agent_memory_brain-0.2.0.dist-info/RECORD +54 -0
- agent_memory_brain-0.2.0.dist-info/WHEEL +4 -0
- agent_memory_brain-0.2.0.dist-info/entry_points.txt +3 -0
- agent_memory_brain-0.2.0.dist-info/licenses/LICENSE +67 -0
- devmem/__init__.py +3 -0
- devmem/ado.py +433 -0
- devmem/agent_colors.py +74 -0
- devmem/cli.py +500 -0
- devmem/db.py +1069 -0
- devmem/dispatch.py +478 -0
- devmem/embed.py +111 -0
- devmem/filelock.py +53 -0
- devmem/git_sync.py +261 -0
- devmem/guard.py +214 -0
- devmem/hook_log.py +49 -0
- devmem/ingest/__init__.py +1 -0
- devmem/ingest/claude.py +87 -0
- devmem/ingest/codex.py +175 -0
- devmem/ingest/runner.py +318 -0
- devmem/ingest/subagents.py +250 -0
- devmem/maintenance.py +801 -0
- devmem/mcp_server.py +542 -0
- devmem/messages.py +684 -0
- devmem/panel/__init__.py +1 -0
- devmem/panel/app.py +971 -0
- devmem/panel/state.py +358 -0
- devmem/panel/static/base.css +27 -0
- devmem/panel/static/brain.css +473 -0
- devmem/panel/static/brain.js +1118 -0
- devmem/panel/static/brain3d.js +600 -0
- devmem/panel/static/chat.js +18 -0
- devmem/panel/static/common.js +208 -0
- devmem/panel/static/site.css +508 -0
- devmem/panel/static/tokens.css +19 -0
- devmem/panel/templates/ado.html +62 -0
- devmem/panel/templates/agents.html +35 -0
- devmem/panel/templates/audit.html +19 -0
- devmem/panel/templates/base.html +42 -0
- devmem/panel/templates/brain.html +101 -0
- devmem/panel/templates/maintenance.html +32 -0
- devmem/panel/templates/memory.html +71 -0
- devmem/panel/templates/message_thread.html +49 -0
- devmem/panel/templates/messages.html +8 -0
- devmem/panel/templates/overview.html +109 -0
- devmem/panel/templates/project.html +48 -0
- devmem/panel/templates/projects.html +21 -0
- devmem/panel/templates/session.html +49 -0
- devmem/panel/templates/sessions.html +67 -0
- devmem/panel/templates/subagent.html +27 -0
- devmem/procs.py +10 -0
- devmem/projects.py +17 -0
- devmem/secrets.py +9 -0
- devmem/skills.py +105 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// Shared helpers for devmem pages (no dependencies).
|
|
2
|
+
(function () {
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
function fmtTokens(n) {
|
|
6
|
+
n = Number(n) || 0;
|
|
7
|
+
if (n >= 1e9) return (n / 1e9).toFixed(n >= 1e10 ? 0 : 1) + "B";
|
|
8
|
+
if (n >= 1e6) return (n / 1e6).toFixed(n >= 1e7 ? 0 : 1) + "M";
|
|
9
|
+
if (n >= 1e3) return (n / 1e3).toFixed(n >= 1e4 ? 0 : 1) + "k";
|
|
10
|
+
return String(Math.round(n));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function parseTs(value) {
|
|
14
|
+
if (value === null || value === undefined || value === "") return null;
|
|
15
|
+
if (typeof value === "number" || /^\d+(\.\d+)?$/.test(String(value))) return new Date(Number(value) * 1000);
|
|
16
|
+
var s = String(value).replace(" ", "T");
|
|
17
|
+
if (!/[zZ]|[+-]\d\d:?\d\d$/.test(s)) s += "Z";
|
|
18
|
+
var d = new Date(s);
|
|
19
|
+
return isNaN(d) ? null : d;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function fmtDuration(seconds) {
|
|
23
|
+
seconds = Math.max(0, Math.round(seconds));
|
|
24
|
+
var d = Math.floor(seconds / 86400), h = Math.floor((seconds % 86400) / 3600),
|
|
25
|
+
m = Math.floor((seconds % 3600) / 60), s = seconds % 60;
|
|
26
|
+
if (d) return d + "d " + h + "h";
|
|
27
|
+
if (h) return h + "h " + String(m).padStart(2, "0") + "m";
|
|
28
|
+
if (m) return m + "m " + String(s).padStart(2, "0") + "s";
|
|
29
|
+
return s + "s";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function relTime(value) {
|
|
33
|
+
var d = value instanceof Date ? value : parseTs(value);
|
|
34
|
+
if (!d) return "—";
|
|
35
|
+
var s = (Date.now() - d.getTime()) / 1000;
|
|
36
|
+
if (s < 5) return "now";
|
|
37
|
+
if (s < 60) return Math.round(s) + "s ago";
|
|
38
|
+
if (s < 3600) return Math.round(s / 60) + "m ago";
|
|
39
|
+
if (s < 86400) return Math.round(s / 3600) + "h ago";
|
|
40
|
+
return Math.round(s / 86400) + "d ago";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Simple stacked bar chart into an <svg>; rows: [{x, series: {name: value}}]
|
|
44
|
+
function barChart(svg, rows, colors) {
|
|
45
|
+
var w = svg.clientWidth || 800, h = svg.clientHeight || 220, pad = 28;
|
|
46
|
+
var max = 1;
|
|
47
|
+
rows.forEach(function (r) {
|
|
48
|
+
var t = 0; for (var k in r.series) t += r.series[k]; max = Math.max(max, t);
|
|
49
|
+
});
|
|
50
|
+
var bw = (w - pad) / Math.max(rows.length, 1);
|
|
51
|
+
var parts = ['<line x1="' + pad + '" y1="' + (h - 18) + '" x2="' + w + '" y2="' + (h - 18) + '" stroke="#24211c"/>',
|
|
52
|
+
'<text x="0" y="12" fill="#9c9890" font-size="11">' + fmtTokens(max) + "</text>"];
|
|
53
|
+
rows.forEach(function (r, i) {
|
|
54
|
+
var y = h - 18;
|
|
55
|
+
Object.keys(r.series).forEach(function (k) {
|
|
56
|
+
var bh = (r.series[k] / max) * (h - 36);
|
|
57
|
+
y -= bh;
|
|
58
|
+
parts.push('<rect x="' + (pad + i * bw + 1) + '" y="' + y + '" width="' + Math.max(1, bw - 3) + '" height="' + bh +
|
|
59
|
+
'" rx="0" fill="' + (colors[k] || "#9c9890") + '"><title>' + r.x + " " + k + ": " + fmtTokens(r.series[k]) + "</title></rect>");
|
|
60
|
+
});
|
|
61
|
+
if (rows.length < 16 || i % Math.ceil(rows.length / 10) === 0)
|
|
62
|
+
parts.push('<text x="' + (pad + i * bw) + '" y="' + (h - 4) + '" fill="#66635d" font-size="10">' + String(r.x).slice(5) + "</text>");
|
|
63
|
+
});
|
|
64
|
+
svg.setAttribute("viewBox", "0 0 " + w + " " + h);
|
|
65
|
+
svg.innerHTML = parts.join("");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
var AGENT_COLORS = { claude: "#d6c1a4", "claude-subagent": "#998a75", codex: "#6db0f4" };
|
|
69
|
+
var byId = function (id) { return document.getElementById(id); };
|
|
70
|
+
|
|
71
|
+
function agentColor(agent) {
|
|
72
|
+
if (AGENT_COLORS[agent]) return AGENT_COLORS[agent];
|
|
73
|
+
agent = String(agent || "");
|
|
74
|
+
if (agent.indexOf("codex") >= 0) return AGENT_COLORS.codex;
|
|
75
|
+
if (agent.indexOf("claude") >= 0) return AGENT_COLORS.claude;
|
|
76
|
+
return "#9c9890";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function agentShort(agent) {
|
|
80
|
+
return agent === "claude-subagent" ? "subagent" : String(agent || "agent");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function esc(s) {
|
|
84
|
+
return String(s === null || s === undefined ? "" : s).replace(/[&<>"']/g, function (c) {
|
|
85
|
+
return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c];
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Token block, shown on the Overview page (it moved there from the Brain wall).
|
|
90
|
+
|
|
91
|
+
function splitHtml(map) {
|
|
92
|
+
var agents = Object.keys(map || {}).sort();
|
|
93
|
+
if (!agents.length) return '<div class="empty">no usage</div>';
|
|
94
|
+
return agents.map(function (a) {
|
|
95
|
+
return '<div style="--c:' + agentColor(a) + '"><span>' + esc(agentShort(a)) + "</span><b>" + fmtTokens(map[a].total) + "</b></div>";
|
|
96
|
+
}).join("");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function renderSpark(hourly) {
|
|
100
|
+
var svg = byId("spark");
|
|
101
|
+
var w = svg.clientWidth || 300, h = svg.clientHeight || 80;
|
|
102
|
+
svg.setAttribute("viewBox", "0 0 " + w + " " + h);
|
|
103
|
+
var series = hourly.series || {};
|
|
104
|
+
var agents = Object.keys(series).sort();
|
|
105
|
+
var max = 1;
|
|
106
|
+
agents.forEach(function (a) { series[a].forEach(function (v) { max = Math.max(max, v); }); });
|
|
107
|
+
var step = w / 23, out = [];
|
|
108
|
+
out.push('<line x1="0" y1="' + (h - 1) + '" x2="' + w + '" y2="' + (h - 1) + '" stroke="#24211c"/>');
|
|
109
|
+
agents.forEach(function (a, idx) {
|
|
110
|
+
var c = agentColor(a);
|
|
111
|
+
var pts = series[a].map(function (v, i) { return [i * step, h - 3 - (v / max) * (h - 10)]; });
|
|
112
|
+
var line = pts.map(function (p) { return p[0].toFixed(1) + "," + p[1].toFixed(1); }).join(" ");
|
|
113
|
+
out.push('<defs><linearGradient id="sg' + idx + '" x1="0" x2="0" y1="0" y2="1"><stop offset="0" stop-color="' + c + '" stop-opacity=".14"/>' +
|
|
114
|
+
'<stop offset="1" stop-color="' + c + '" stop-opacity="0"/></linearGradient></defs>');
|
|
115
|
+
out.push('<polygon points="0,' + h + " " + line + " " + w + "," + h + '" fill="url(#sg' + idx + ')"/>');
|
|
116
|
+
out.push('<polyline points="' + line + '" fill="none" stroke="' + c + '" stroke-width="1.25" stroke-linejoin="miter"/>');
|
|
117
|
+
});
|
|
118
|
+
out.push('<text x="2" y="11" fill="#66635d" font-size="10">' + fmtTokens(max) + "/h</text>");
|
|
119
|
+
if (!agents.length) out.push('<text x="' + w / 2 + '" y="' + h / 2 + '" fill="#66635d" font-size="12" text-anchor="middle">no usage in 24 h</text>');
|
|
120
|
+
svg.innerHTML = out.join("");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function renderTokens(t) {
|
|
124
|
+
byId("tok-today").textContent = fmtTokens(t.today_total);
|
|
125
|
+
byId("tok-week").textContent = fmtTokens(t.week_total);
|
|
126
|
+
byId("tok-today-split").innerHTML = splitHtml(t.today);
|
|
127
|
+
byId("tok-week-split").innerHTML = splitHtml(t.week);
|
|
128
|
+
renderSpark(t.hourly || {});
|
|
129
|
+
var models = (t.models_today || []).slice(0, 5);
|
|
130
|
+
byId("models").innerHTML = models.length ? models.map(function (m) {
|
|
131
|
+
var tot = Math.max(1, m.total);
|
|
132
|
+
var seg = function (cls, v) { return '<i class="' + cls + '" style="width:' + (v / tot * 100).toFixed(2) + '%"></i>'; };
|
|
133
|
+
return '<li style="--c:' + agentColor(m.agent) + '" title="in ' + m.input + " · out " + m.output + " · cache read " + m.cache_read + " · cache write " + m.cache_write + '">' +
|
|
134
|
+
'<span class="m-name">' + esc(m.model) + '</span><span class="m-total">' + fmtTokens(m.total) + "</span>" +
|
|
135
|
+
'<span class="seg">' + seg("s-in", m.input) + seg("s-out", m.output) + seg("s-cr", m.cache_read) + seg("s-cw", m.cache_write) + "</span></li>";
|
|
136
|
+
}).join("") : '<li class="empty">no usage today</li>';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
window.devmem = { fmtTokens: fmtTokens, parseTs: parseTs, fmtDuration: fmtDuration, relTime: relTime, barChart: barChart,
|
|
140
|
+
renderTokens: renderTokens, renderSpark: renderSpark, agentColors: AGENT_COLORS };
|
|
141
|
+
|
|
142
|
+
// Render any <time data-rel> as relative time.
|
|
143
|
+
document.querySelectorAll("time[data-rel]").forEach(function (el) {
|
|
144
|
+
el.title = el.getAttribute("datetime") || "";
|
|
145
|
+
el.textContent = relTime(el.getAttribute("datetime"));
|
|
146
|
+
});
|
|
147
|
+
document.querySelectorAll("[data-tokens]").forEach(function (el) {
|
|
148
|
+
el.title = el.getAttribute("data-tokens");
|
|
149
|
+
el.textContent = fmtTokens(el.getAttribute("data-tokens"));
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
var adoLink = document.querySelector('a[href="/ado"]');
|
|
153
|
+
var adoBadge = adoLink && adoLink.querySelector(".ado-change-badge");
|
|
154
|
+
var adoChanges = document.getElementById("ado-changes");
|
|
155
|
+
|
|
156
|
+
// Stored as "<journal_id>:<seq>"; a seq from an earlier panel run (other journal_id) counts as nothing seen.
|
|
157
|
+
function seenAdoSeq(journalId) {
|
|
158
|
+
try {
|
|
159
|
+
var match = /^([0-9a-f]+):(\d+)$/.exec(localStorage.getItem("devmem.ado.seen") || "");
|
|
160
|
+
return match && match[1] === journalId ? Number(match[2]) : 0;
|
|
161
|
+
} catch (_) { return 0; }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function setSeenAdoSeq(journalId, seq) {
|
|
165
|
+
try { localStorage.setItem("devmem.ado.seen", journalId + ":" + seq); } catch (_) {}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function showAdoChanges(changes) {
|
|
169
|
+
if (!adoChanges) return;
|
|
170
|
+
adoChanges.textContent = "";
|
|
171
|
+
if (!changes.length) { adoChanges.hidden = true; return; }
|
|
172
|
+
var heading = document.createElement("strong");
|
|
173
|
+
heading.textContent = "Changed since you last looked";
|
|
174
|
+
var list = document.createElement("ul");
|
|
175
|
+
changes.forEach(function (change) {
|
|
176
|
+
var item = document.createElement("li"), text = document.createElement("span"), at = document.createElement("time");
|
|
177
|
+
text.textContent = change.text;
|
|
178
|
+
at.setAttribute("datetime", change.at);
|
|
179
|
+
at.setAttribute("data-rel", "");
|
|
180
|
+
at.title = change.at;
|
|
181
|
+
at.textContent = relTime(change.at);
|
|
182
|
+
item.append(text, at); list.appendChild(item);
|
|
183
|
+
});
|
|
184
|
+
adoChanges.append(heading, list); adoChanges.hidden = false;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// On the ADO page the box keeps what was unseen when the page opened, plus anything newer.
|
|
188
|
+
var adoPageSince = null;
|
|
189
|
+
|
|
190
|
+
function refreshAdoChanges() {
|
|
191
|
+
fetch("/api/ado/changes").then(function (response) {
|
|
192
|
+
if (!response.ok) throw new Error("ADO changes failed");
|
|
193
|
+
return response.json();
|
|
194
|
+
}).then(function (data) {
|
|
195
|
+
var seen = seenAdoSeq(data.journal_id), latest = data.latest_seq || 0;
|
|
196
|
+
if (adoBadge) adoBadge.hidden = !(latest > seen);
|
|
197
|
+
if (adoChanges) {
|
|
198
|
+
if (adoPageSince === null) adoPageSince = seen;
|
|
199
|
+
showAdoChanges((data.changes || []).filter(function (change) { return change.seq > adoPageSince; }));
|
|
200
|
+
setSeenAdoSeq(data.journal_id, latest);
|
|
201
|
+
if (adoBadge) adoBadge.hidden = true;
|
|
202
|
+
}
|
|
203
|
+
}).catch(function () {});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
refreshAdoChanges();
|
|
207
|
+
setInterval(refreshAdoChanges, 30000);
|
|
208
|
+
})();
|