monomind 2.1.0 → 2.1.4
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/README.md +77 -94
- package/package.json +3 -3
- package/packages/@monomind/cli/.claude/commands/mastermind/createorg.md +8 -9
- package/packages/@monomind/cli/.claude/helpers/handlers/capture-handler.cjs +24 -10
- package/packages/@monomind/cli/.claude/helpers/handlers/gates-handler.cjs +2 -2
- package/packages/@monomind/cli/.claude/helpers/handlers/route-handler.cjs +29 -0
- package/packages/@monomind/cli/.claude/helpers/handlers/session-handler.cjs +35 -23
- package/packages/@monomind/cli/.claude/helpers/hook-handler.cjs +21 -0
- package/packages/@monomind/cli/.claude/helpers/utils/monograph.cjs +73 -2
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/createorg.md +91 -701
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/org-settings.md +46 -47
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/runorg.md +12 -1
- package/packages/@monomind/cli/README.md +77 -94
- package/packages/@monomind/cli/dist/src/browser/workflow/store.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/commands/browse.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.js +5 -0
- package/packages/@monomind/cli/dist/src/commands/org.js +46 -29
- package/packages/@monomind/cli/dist/src/commands/platforms.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/init/executor.js +30 -6
- package/packages/@monomind/cli/dist/src/mcp-tools/auto-install.d.ts +8 -8
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence/types.d.ts +31 -31
- package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +5 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +28 -3
- package/packages/@monomind/cli/dist/src/mcp-tools/quality/coverage-analysis/prioritize-gaps.d.ts +36 -36
- package/packages/@monomind/cli/dist/src/mcp-tools/quality/security-compliance/detect-secrets.d.ts +4 -4
- package/packages/@monomind/cli/dist/src/memory/ewc-consolidation.d.ts +12 -0
- package/packages/@monomind/cli/dist/src/memory/sona-optimizer.d.ts +12 -0
- package/packages/@monomind/cli/dist/src/orgrt/broker.d.ts +3 -1
- package/packages/@monomind/cli/dist/src/orgrt/broker.js +8 -3
- package/packages/@monomind/cli/dist/src/orgrt/bus.d.ts +5 -1
- package/packages/@monomind/cli/dist/src/orgrt/bus.js +5 -1
- package/packages/@monomind/cli/dist/src/orgrt/daemon.d.ts +35 -1
- package/packages/@monomind/cli/dist/src/orgrt/daemon.js +190 -21
- package/packages/@monomind/cli/dist/src/orgrt/forwarder.js +47 -10
- package/packages/@monomind/cli/dist/src/orgrt/inbox.d.ts +11 -0
- package/packages/@monomind/cli/dist/src/orgrt/inbox.js +56 -0
- package/packages/@monomind/cli/dist/src/orgrt/policy.d.ts +5 -1
- package/packages/@monomind/cli/dist/src/orgrt/policy.js +58 -5
- package/packages/@monomind/cli/dist/src/orgrt/server.d.ts +3 -2
- package/packages/@monomind/cli/dist/src/orgrt/server.js +31 -40
- package/packages/@monomind/cli/dist/src/orgrt/session.d.ts +11 -1
- package/packages/@monomind/cli/dist/src/orgrt/session.js +32 -1
- package/packages/@monomind/cli/dist/src/orgrt/test-loop.js +8 -8
- package/packages/@monomind/cli/dist/src/orgrt/types.d.ts +53 -53
- package/packages/@monomind/cli/dist/src/output.d.ts +29 -29
- package/packages/@monomind/cli/dist/src/output.js +10 -2
- package/packages/@monomind/cli/dist/src/pricing/model-pricing.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/types.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/ui/collector.mjs +20 -5
- package/packages/@monomind/cli/dist/src/ui/dashboard.html +465 -500
- package/packages/@monomind/cli/dist/src/ui/orgs-files.js +192 -0
- package/packages/@monomind/cli/dist/src/ui/orgs.html +21 -52
- package/packages/@monomind/cli/dist/src/ui/server.mjs +174 -164
- package/packages/@monomind/cli/package.json +12 -16
- package/packages/@monomind/cli/scripts/publish.sh +6 -0
- package/scripts/generate-agent-avatars.mjs +3 -3
- package/packages/@monomind/cli/dist/src/orgrt/live.html +0 -56
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// packages/@monomind/cli/src/ui/orgs-files.js
|
|
2
|
+
// Files tab + diff-view feature, split out of orgs.html to keep that file under
|
|
3
|
+
// the CLAUDE.md 500-line guideline. Loaded as a classic <script> right where this
|
|
4
|
+
// code used to live inline — shares the same global scope as orgs.html's main
|
|
5
|
+
// script (chatSessions, orgSessionMatch, esc, currentTab, loadChatSessions,
|
|
6
|
+
// viewArtifact are all defined there and referenced here by closure).
|
|
7
|
+
|
|
8
|
+
// ── Files tab: every asset produced by this org, grouped by path with full
|
|
9
|
+
// version history (not deduped) so successive writes to the same file can be
|
|
10
|
+
// diffed against each other, latest-touched file first ──
|
|
11
|
+
let filesGroups = [];
|
|
12
|
+
|
|
13
|
+
function collectOrgArtifacts() {
|
|
14
|
+
const bySessions = chatSessions.filter(orgSessionMatch);
|
|
15
|
+
const byPath = new Map(); // path -> [{ art, ts, from }]
|
|
16
|
+
bySessions.forEach(s => (s.events || []).forEach(ev => {
|
|
17
|
+
if (ev.type !== 'org:artifact') return;
|
|
18
|
+
const art = ev.artifact || (ev.path ? { path: ev.path, label: ev.label, mimeType: ev.mimeType } : null);
|
|
19
|
+
if (!art || !art.path) return;
|
|
20
|
+
if (!byPath.has(art.path)) byPath.set(art.path, []);
|
|
21
|
+
byPath.get(art.path).push({ art, ts: ev.ts || 0, from: ev.from || '' });
|
|
22
|
+
}));
|
|
23
|
+
const groups = [];
|
|
24
|
+
byPath.forEach((versions, path) => {
|
|
25
|
+
versions.sort((a, b) => a.ts - b.ts);
|
|
26
|
+
groups.push({ path, versions, latest: versions[versions.length - 1] });
|
|
27
|
+
});
|
|
28
|
+
groups.sort((a, b) => b.latest.ts - a.latest.ts);
|
|
29
|
+
return groups;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function mkFileCard(group) {
|
|
33
|
+
const { art, ts, from } = group.latest;
|
|
34
|
+
const el = document.createElement('div');
|
|
35
|
+
el.className = 'file-card';
|
|
36
|
+
const isText = (art.mimeType || '').startsWith('text/') || (art.mimeType || '') === 'application/json';
|
|
37
|
+
const labelRaw = art.label || (art.path || 'file').split('/').pop();
|
|
38
|
+
const timeStr = ts ? new Date(ts).toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : '';
|
|
39
|
+
const meta = [from, art.path ? art.path.split('/').slice(-3).join('/') : null, timeStr].filter(Boolean).join(' · ');
|
|
40
|
+
const vBadge = group.versions.length > 1 ? `<span class="fc-vbadge">${group.versions.length}v</span>` : '';
|
|
41
|
+
const diffable = group.versions.filter(v => typeof v.art.content === 'string').length >= 2;
|
|
42
|
+
el.innerHTML = `
|
|
43
|
+
<div class="fc-icon">📄</div>
|
|
44
|
+
<div class="fc-body">
|
|
45
|
+
<div class="fc-name">${esc(labelRaw)} ${vBadge}</div>
|
|
46
|
+
<div class="fc-meta">${esc(meta)}</div>
|
|
47
|
+
</div>
|
|
48
|
+
${diffable ? `<button class="fc-diff" onclick="openDiffPanel(${JSON.stringify(art.path)})">Diff</button>` : ''}
|
|
49
|
+
${isText && art.path
|
|
50
|
+
? `<button class="fc-view" onclick="viewArtifact(${JSON.stringify(art.path)},${JSON.stringify(labelRaw)})">View</button>`
|
|
51
|
+
: `<span class="fc-binary">Binary</span>`}
|
|
52
|
+
`;
|
|
53
|
+
return el;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function paintFilesGrid() {
|
|
57
|
+
const grid = document.getElementById('files-grid');
|
|
58
|
+
const empty = document.getElementById('files-empty');
|
|
59
|
+
if (!grid) return;
|
|
60
|
+
filesGroups = collectOrgArtifacts();
|
|
61
|
+
renderFilesGridFromGroups();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Re-renders the grid from the current in-memory `filesGroups` — no rescan.
|
|
65
|
+
* Split out of paintFilesGrid() so applyArtifactEvent() can repaint after an
|
|
66
|
+
* O(groups) incremental update instead of forcing a full O(all-events) rescan. */
|
|
67
|
+
function renderFilesGridFromGroups() {
|
|
68
|
+
const grid = document.getElementById('files-grid');
|
|
69
|
+
const empty = document.getElementById('files-empty');
|
|
70
|
+
if (!grid) return;
|
|
71
|
+
grid.innerHTML = '';
|
|
72
|
+
if (!filesGroups.length) { if (empty) empty.style.display = 'block'; return; }
|
|
73
|
+
if (empty) empty.style.display = 'none';
|
|
74
|
+
filesGroups.forEach(group => grid.appendChild(mkFileCard(group)));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Incrementally folds a single new org:artifact event into `filesGroups` —
|
|
78
|
+
* O(groups) to re-sort by latest-touched, not O(all sessions' all events)
|
|
79
|
+
* like collectOrgArtifacts(). Mirrors collectOrgArtifacts()'s grouping rules. */
|
|
80
|
+
function applyArtifactEvent(ev) {
|
|
81
|
+
const art = ev.artifact || (ev.path ? { path: ev.path, label: ev.label, mimeType: ev.mimeType } : null);
|
|
82
|
+
if (!art || !art.path) return;
|
|
83
|
+
const entry = { art, ts: ev.ts || 0, from: ev.from || '' };
|
|
84
|
+
let group = filesGroups.find(g => g.path === art.path);
|
|
85
|
+
if (!group) {
|
|
86
|
+
group = { path: art.path, versions: [], latest: entry };
|
|
87
|
+
filesGroups.push(group);
|
|
88
|
+
}
|
|
89
|
+
group.versions.push(entry);
|
|
90
|
+
group.versions.sort((a, b) => a.ts - b.ts);
|
|
91
|
+
group.latest = group.versions[group.versions.length - 1];
|
|
92
|
+
filesGroups.sort((a, b) => b.latest.ts - a.latest.ts);
|
|
93
|
+
renderFilesGridFromGroups();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function renderFilesTab() {
|
|
97
|
+
paintFilesGrid(); // paint immediately with whatever's cached
|
|
98
|
+
loadChatSessions().then(() => { if (currentTab === 'files') paintFilesGrid(); });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ── Diff view: compare two captured versions of the same file ──
|
|
102
|
+
window.openDiffPanel = function(path) {
|
|
103
|
+
const group = filesGroups.find(g => g.path === path);
|
|
104
|
+
if (!group) return;
|
|
105
|
+
const versions = group.versions.filter(v => typeof v.art.content === 'string');
|
|
106
|
+
if (versions.length < 2) return;
|
|
107
|
+
const label = group.latest.art.label || path.split('/').pop();
|
|
108
|
+
renderDiffPanel(label, versions);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
function renderDiffPanel(label, versions) {
|
|
112
|
+
let panel = document.getElementById('artifact-panel');
|
|
113
|
+
if (!panel) {
|
|
114
|
+
panel = document.createElement('div');
|
|
115
|
+
panel.id = 'artifact-panel';
|
|
116
|
+
panel.style.cssText = 'position:fixed;top:0;right:0;width:640px;height:100vh;background:#0a0a14;border-left:1px solid #333;z-index:1000;display:flex;flex-direction:column;overflow:hidden';
|
|
117
|
+
document.body.appendChild(panel);
|
|
118
|
+
}
|
|
119
|
+
const optLabel = v => `${new Date(v.ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })} · ${esc(v.from || '?')}`;
|
|
120
|
+
const opts = versions.map((v, i) => `<option value="${i}">${optLabel(v)}</option>`).join('');
|
|
121
|
+
panel.innerHTML = `
|
|
122
|
+
<div style="padding:12px 14px;border-bottom:1px solid #1a1a2a;display:flex;align-items:center;gap:10px">
|
|
123
|
+
<span style="font-size:13px;font-weight:700;color:#ccc;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(label)} — diff</span>
|
|
124
|
+
<button onclick="document.getElementById('artifact-panel').remove()" style="background:none;border:none;color:#888;font-size:16px;cursor:pointer">✕</button>
|
|
125
|
+
</div>
|
|
126
|
+
<div style="padding:8px 14px;border-bottom:1px solid #1a1a2a;display:flex;gap:8px;align-items:center;font-size:10px;color:#888">
|
|
127
|
+
<select id="diff-from" style="background:#151520;color:#ccc;border:1px solid #333;border-radius:3px;padding:2px 6px;font-size:10px">${opts}</select>
|
|
128
|
+
<span>→</span>
|
|
129
|
+
<select id="diff-to" style="background:#151520;color:#ccc;border:1px solid #333;border-radius:3px;padding:2px 6px;font-size:10px">${opts}</select>
|
|
130
|
+
</div>
|
|
131
|
+
<div id="diff-body" style="flex:1;overflow:auto;font-size:11px;line-height:1.6;font-family:var(--mono,ui-monospace,monospace)"></div>
|
|
132
|
+
`;
|
|
133
|
+
panel.style.display = 'flex';
|
|
134
|
+
const fromSel = document.getElementById('diff-from');
|
|
135
|
+
const toSel = document.getElementById('diff-to');
|
|
136
|
+
fromSel.value = String(versions.length - 2);
|
|
137
|
+
toSel.value = String(versions.length - 1);
|
|
138
|
+
const recompute = () => renderDiffBody(versions[Number(fromSel.value)].art.content, versions[Number(toSel.value)].art.content);
|
|
139
|
+
fromSel.onchange = recompute;
|
|
140
|
+
toSel.onchange = recompute;
|
|
141
|
+
recompute();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function renderDiffBody(oldText, newText) {
|
|
145
|
+
const body = document.getElementById('diff-body');
|
|
146
|
+
if (!body) return;
|
|
147
|
+
if (oldText === newText) {
|
|
148
|
+
body.innerHTML = '<div style="padding:14px;color:#666">No differences between these two versions.</div>';
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const rows = diffLines(oldText, newText);
|
|
152
|
+
if (!rows) {
|
|
153
|
+
body.innerHTML = '<div style="padding:14px;color:#666">File too large to diff line-by-line.</div>';
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
body.innerHTML = rows.map(r => {
|
|
157
|
+
const style = r.type === 'add' ? 'background:#0d2818;color:#7ee787'
|
|
158
|
+
: r.type === 'del' ? 'background:#2d0d10;color:#ffa198' : 'color:#8b949e';
|
|
159
|
+
const prefix = r.type === 'add' ? '+' : r.type === 'del' ? '-' : ' ';
|
|
160
|
+
return `<div style="${style};padding:1px 12px;white-space:pre-wrap;word-break:break-word">${esc(prefix + ' ' + r.text)}</div>`;
|
|
161
|
+
}).join('');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Line-level LCS diff. Returns null (caller shows a fallback message) rather
|
|
165
|
+
* than hanging the tab if both files are unexpectedly huge — the O(n*m) DP
|
|
166
|
+
* table would otherwise blow past what's reasonable to allocate client-side.
|
|
167
|
+
* Bounds n and m individually, not just their product: a lopsided pair (e.g.
|
|
168
|
+
* n=1, m=4000000 — one huge file diffed against a near-empty one) passes the
|
|
169
|
+
* product check but still allocates a 4M-row DP table, one row per line of A. */
|
|
170
|
+
function diffLines(a, b) {
|
|
171
|
+
const A = a.split('\n'), B = b.split('\n');
|
|
172
|
+
const n = A.length, m = B.length;
|
|
173
|
+
const DIFF_MAX_LINES = 20000;
|
|
174
|
+
if (n > DIFF_MAX_LINES || m > DIFF_MAX_LINES || n * m > 4000000) return null;
|
|
175
|
+
const dp = new Array(n + 1);
|
|
176
|
+
for (let i = 0; i <= n; i++) dp[i] = new Int32Array(m + 1);
|
|
177
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
178
|
+
for (let j = m - 1; j >= 0; j--) {
|
|
179
|
+
dp[i][j] = A[i] === B[j] ? dp[i + 1][j + 1] + 1 : Math.max(dp[i + 1][j], dp[i][j + 1]);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const out = [];
|
|
183
|
+
let i = 0, j = 0;
|
|
184
|
+
while (i < n && j < m) {
|
|
185
|
+
if (A[i] === B[j]) { out.push({ type: 'ctx', text: A[i] }); i++; j++; }
|
|
186
|
+
else if (dp[i + 1][j] >= dp[i][j + 1]) { out.push({ type: 'del', text: A[i] }); i++; }
|
|
187
|
+
else { out.push({ type: 'add', text: B[j] }); j++; }
|
|
188
|
+
}
|
|
189
|
+
while (i < n) { out.push({ type: 'del', text: A[i] }); i++; }
|
|
190
|
+
while (j < m) { out.push({ type: 'add', text: B[j] }); j++; }
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
@@ -412,6 +412,15 @@ html, body {
|
|
|
412
412
|
border-radius: 4px; padding: 4px 10px; font-size: 10px; font-weight: 700; cursor: pointer;
|
|
413
413
|
}
|
|
414
414
|
.fc-view:hover { background: #204a70; }
|
|
415
|
+
.fc-diff {
|
|
416
|
+
flex-shrink: 0; background: #2a1a3a; color: #b98dd9; border: 1px solid #8e44ad44;
|
|
417
|
+
border-radius: 4px; padding: 4px 10px; font-size: 10px; font-weight: 700; cursor: pointer;
|
|
418
|
+
}
|
|
419
|
+
.fc-diff:hover { background: #3a2050; }
|
|
420
|
+
.fc-vbadge {
|
|
421
|
+
font-size: 8px; font-weight: 700; color: var(--dim); border: 1px solid var(--border);
|
|
422
|
+
border-radius: 2px; padding: 1px 5px; margin-left: 4px; letter-spacing: 0.5px;
|
|
423
|
+
}
|
|
415
424
|
.fc-binary { flex-shrink: 0; font-size: 9px; color: var(--dim); }
|
|
416
425
|
|
|
417
426
|
/* ── Activity / event log ───────────────────────────────────────── */
|
|
@@ -1125,56 +1134,14 @@ window.switchTab = function(tab) {
|
|
|
1125
1134
|
else if (orgDetailData) renderTab(tab);
|
|
1126
1135
|
};
|
|
1127
1136
|
|
|
1128
|
-
//
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
const prev = seen.get(art.path);
|
|
1137
|
-
if (!prev || (ev.ts || 0) >= prev.ts) seen.set(art.path, { art, ts: ev.ts || 0, from: ev.from || '' });
|
|
1138
|
-
}));
|
|
1139
|
-
return Array.from(seen.values()).sort((a, b) => b.ts - a.ts);
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
function mkFileCard(entry) {
|
|
1143
|
-
const { art, ts, from } = entry;
|
|
1144
|
-
const el = document.createElement('div');
|
|
1145
|
-
el.className = 'file-card';
|
|
1146
|
-
const isText = (art.mimeType || '').startsWith('text/') || (art.mimeType || '') === 'application/json';
|
|
1147
|
-
const labelRaw = art.label || (art.path || 'file').split('/').pop();
|
|
1148
|
-
const timeStr = ts ? new Date(ts).toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : '';
|
|
1149
|
-
const meta = [from, art.path ? art.path.split('/').slice(-3).join('/') : null, timeStr].filter(Boolean).join(' · ');
|
|
1150
|
-
el.innerHTML = `
|
|
1151
|
-
<div class="fc-icon">📄</div>
|
|
1152
|
-
<div class="fc-body">
|
|
1153
|
-
<div class="fc-name">${esc(labelRaw)}</div>
|
|
1154
|
-
<div class="fc-meta">${esc(meta)}</div>
|
|
1155
|
-
</div>
|
|
1156
|
-
${isText && art.path
|
|
1157
|
-
? `<button class="fc-view" onclick="viewArtifact(${JSON.stringify(art.path)},${JSON.stringify(labelRaw)})">View</button>`
|
|
1158
|
-
: `<span class="fc-binary">Binary</span>`}
|
|
1159
|
-
`;
|
|
1160
|
-
return el;
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
function paintFilesGrid() {
|
|
1164
|
-
const grid = document.getElementById('files-grid');
|
|
1165
|
-
const empty = document.getElementById('files-empty');
|
|
1166
|
-
if (!grid) return;
|
|
1167
|
-
const files = collectOrgArtifacts();
|
|
1168
|
-
grid.innerHTML = '';
|
|
1169
|
-
if (!files.length) { if (empty) empty.style.display = 'block'; return; }
|
|
1170
|
-
if (empty) empty.style.display = 'none';
|
|
1171
|
-
files.forEach(entry => grid.appendChild(mkFileCard(entry)));
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
function renderFilesTab() {
|
|
1175
|
-
paintFilesGrid(); // paint immediately with whatever's cached
|
|
1176
|
-
loadChatSessions().then(() => { if (currentTab === 'files') paintFilesGrid(); });
|
|
1177
|
-
}
|
|
1137
|
+
// Files tab + diff view — extracted to orgs-files.js to keep this file under
|
|
1138
|
+
// the CLAUDE.md 500-line-per-file guideline. Loaded as a classic script below,
|
|
1139
|
+
// sharing this script's global scope (function declarations here call into it,
|
|
1140
|
+
// e.g. renderFilesTab() from renderTab() above and applyArtifactEvent() from
|
|
1141
|
+
// handleMmEvent() below).
|
|
1142
|
+
</script>
|
|
1143
|
+
<script src="orgs-files.js"></script>
|
|
1144
|
+
<script>
|
|
1178
1145
|
|
|
1179
1146
|
// ── Render all orgs in sidebar ────────────────────────────────────
|
|
1180
1147
|
function renderSidebar() {
|
|
@@ -2139,8 +2106,10 @@ function handleMmEvent(ev) {
|
|
|
2139
2106
|
appendChatEvent(ev, true);
|
|
2140
2107
|
chatScrollFeed();
|
|
2141
2108
|
}
|
|
2142
|
-
// Live-update the Files tab when a new asset lands while it's open
|
|
2143
|
-
|
|
2109
|
+
// Live-update the Files tab when a new asset lands while it's open — incremental,
|
|
2110
|
+
// not a rescan of every session's every event (that's collectOrgArtifacts()'s job,
|
|
2111
|
+
// reserved for tab-open / initial load in renderFilesTab()).
|
|
2112
|
+
if (ev.type === 'org:artifact' && currentTab === 'files') applyArtifactEvent(ev);
|
|
2144
2113
|
}
|
|
2145
2114
|
}
|
|
2146
2115
|
|