Catalog dedup
Tool duplicates review surface
Pairs ranked by cosine similarity in the tools_semantic Milvus index. Refreshed by daedarch.tools_find_duplicates_v1. Click a pair to see side-by-side code. Pre-generation gating via tools_register_v1 means these are the historical backlog; new tools land with uniqueness_audit stamps that prevent fresh additions to the pile.
— Total pairs
— Exact ≥0.99
— Near 0.92-0.99
— Strong 0.85-0.92
— Similar 0.78-0.85
— Active↔Active
— Active↔Non-active
All
Exact ≥99%
Near ≥92%
Strong ≥85%
Similar ≥78%
Any status combo
Active ↔ Active
Active ↔ Non-active
Non-active ↔ Non-active
—
Triage
All
Keep A
Keep B
Both stubs
Both real
Identical
Needs review
Load more
';
}
function closeModal(){ $('modal-backdrop').classList.remove('open'); }
// Wire interactions
Array.prototype.forEach.call(document.querySelectorAll('.chip.score'), function(c){
c.addEventListener('click', function(){ setBucket(c.dataset.bucket); });
});
Array.prototype.forEach.call(document.querySelectorAll('.chip.verdict'), function(c){
c.addEventListener('click', function(){
var v = c.dataset.verdict;
state.triage_verdict = (v === 'all') ? null : v;
Array.prototype.forEach.call(document.querySelectorAll('.chip.verdict'), function(x){
x.classList.toggle('on', x.dataset.verdict === v);
});
reload();
});
});
$('status-combo').addEventListener('change', function(e){ state.status_combo = e.target.value; reload(); });
var qTimer = null;
$('q').addEventListener('input', function(e){
if (qTimer) clearTimeout(qTimer);
var v = e.target.value;
qTimer = setTimeout(function(){ state.q = v; reload(); }, 280);
});
$('load-more').addEventListener('click', function(){
state.offset += state.limit;
fetchPage(true);
});
$('modal-close').addEventListener('click', closeModal);
$('modal-backdrop').addEventListener('click', function(e){ if (e.target.id === 'modal-backdrop') closeModal(); });
document.addEventListener('keydown', function(e){ if (e.key === 'Escape') closeModal(); });
function getToken(){
try { return localStorage.getItem('aferai_token') || localStorage.getItem('daedarch_api_key') || ''; }
catch(e){ return ''; }
}
async function applyAction(a, b, action, btn){
var msg = $('apply-msg');
msg.className = 'apply-msg';
msg.textContent = 'Applying…';
Array.prototype.forEach.call($('modal-content').querySelectorAll('.btn-apply'), function(x){ x.disabled = true; });
try {
var headers = {'Content-Type':'application/json'};
var token = getToken();
if (token) {
if (token.indexOf('session:') === 0) headers['Authorization'] = 'Bearer ' + token;
else headers['X-API-Key'] = token;
}
var r = await fetch(API + '/api/v4/execute', {
method:'POST', headers: headers, credentials:'include',
body: JSON.stringify({
tool_id: 'daedarch.tools_apply_consolidation_v1',
inputs: { pair_id: a + '|||' + b, action: action, applied_by: 'web' },
}),
});
var d = await r.json();
if (r.status === 401 || r.status === 403){
msg.className = 'apply-msg err';
msg.textContent = 'Not authorized — sign in or set API key';
Array.prototype.forEach.call($('modal-content').querySelectorAll('.btn-apply'), function(x){ x.disabled = false; });
return;
}
var o = (d.result && d.result.output) || d.output || d;
if (o && o.ok){
msg.className = 'apply-msg ok';
msg.textContent = 'Done · archived ' + (o.archived_tool_id || '—');
showToast(action + ' applied: ' + a + ' ↔ ' + b, false);
setTimeout(function(){ closeModal(); reload(); }, 800);
} else {
msg.className = 'apply-msg err';
msg.textContent = 'Error: ' + esc(o && (o.error || o.reason) || 'unknown');
Array.prototype.forEach.call($('modal-content').querySelectorAll('.btn-apply'), function(x){ x.disabled = false; });
}
} catch(e){
msg.className = 'apply-msg err';
msg.textContent = 'Error: ' + (e.message || String(e));
Array.prototype.forEach.call($('modal-content').querySelectorAll('.btn-apply'), function(x){ x.disabled = false; });
}
}
function showToast(text, isErr){
var t = $('toast');
if (!t){
t = document.createElement('div');
t.id = 'toast';
t.className = 'toast';
document.body.appendChild(t);
}
t.className = 'toast show' + (isErr ? ' err' : '');
t.textContent = text;
setTimeout(function(){ t.className = 'toast' + (isErr ? ' err' : ''); }, 3000);
}
async function bulkPreviewOrApply(dryRun){
var msg = $('bp-result');
var confH = parseFloat($('bp-conf-h').value) || 0.8;
var confL = parseFloat($('bp-conf-l').value) || 0.85;
var label = dryRun ? 'Previewing…' : 'Applying…';
msg.className = 'bulk-result show';
msg.textContent = label;
$('bp-preview').disabled = true;
$('bp-apply').disabled = true;
try {
var headers = {'Content-Type':'application/json'};
var token = getToken();
if (token) {
if (token.indexOf('session:') === 0) headers['Authorization'] = 'Bearer ' + token;
else headers['X-API-Key'] = token;
}
var r = await fetch(API + '/api/v4/execute', {
method:'POST', headers: headers, credentials:'include',
body: JSON.stringify({
tool_id: 'daedarch.tools_apply_consolidation_bulk_v1',
inputs: {
dry_run: !!dryRun,
confidence_min: confH,
llm_confidence_min: confL,
use_llm: true,
llm_same_required: true,
limit: 5000,
applied_by: 'web_bulk',
},
}),
});
if (r.status === 401 || r.status === 403){
msg.className = 'bulk-result show err';
msg.textContent = 'Not authorized — set localStorage.aferai_token (session:...) or .daedarch_api_key (X-API-Key).';
return;
}
var d = await r.json();
var o = (d.result && d.result.output) || d.output || d;
if (!o || !o.ok){
msg.className = 'bulk-result show err';
msg.textContent = 'Error: ' + (o && (o.error || o.reason) || JSON.stringify(d).slice(0,300));
return;
}
var s = o.stats || {};
var lines = [
'dry_run: ' + o.dry_run,
'candidates: ' + s.candidates + ' applied: ' + s.applied + ' errors: ' + (s.errors || 0),
'sources — heuristic: ' + ((s.by_source||{}).heuristic||0) + ', llm: ' + ((s.by_source||{}).llm||0),
'actions — archive_a: ' + ((s.by_action||{}).archive_a||0) + ', archive_b: ' + ((s.by_action||{}).archive_b||0),
'skipped — already_actioned: ' + (s.skipped_already_actioned||0) + ', already_archived: ' + (s.skipped_already_archived||0) + ', no_target: ' + (s.skipped_no_target||0),
'',
'top examples:',
];
(s.examples || []).slice(0, 10).forEach(function(ex){
lines.push(' archive ' + ex.archived + ' → keeper ' + ex.keeper + ' (' + ex.source + ', score=' + (ex.score*100).toFixed(2) + '%)');
});
msg.className = 'bulk-result show ok';
msg.textContent = lines.join('\n');
if (!dryRun){
showToast('Applied ' + s.applied + ' archives (bulk)', false);
setTimeout(reload, 1000);
}
} catch(e){
msg.className = 'bulk-result show err';
msg.textContent = 'Error: ' + (e.message || String(e));
} finally {
$('bp-preview').disabled = false;
$('bp-apply').disabled = false;
}
}
document.getElementById('bp-preview').addEventListener('click', function(){ bulkPreviewOrApply(true); });
document.getElementById('bp-apply').addEventListener('click', function(){
var confH = $('bp-conf-h').value;
var confL = $('bp-conf-l').value;
if (!confirm('Bulk archive all pairs above heuristic_confidence=' + confH + ' or llm_confidence=' + confL + '. This is reversible (status set to archived), but affects many tools. Proceed?')) return;
bulkPreviewOrApply(false);
});
reload();
})();
💬