From a606adf038e69e363012c07459feb79966a4ce95 Mon Sep 17 00:00:00 2001 From: Joseph Magly <1159087+jmagly@users.noreply.github.com> Date: Sat, 1 Aug 2026 16:03:57 -0400 Subject: [PATCH] feat(ui): add local-agent disconnect control (#143) --- docs/index.html | 19 ++++++++++++++++++- .../local-api-hardening-static.test.ts | 8 ++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/index.html b/docs/index.html index 7ce7abe..77ef5c5 100755 --- a/docs/index.html +++ b/docs/index.html @@ -26365,7 +26365,8 @@ Be specific and actionable. Format as structured JSON.`; + pill(status, color) + (connected ? '' : '') + (a.ready ? '' : '') - + (a.ready ? '' : '') + + (connected ? '' + : (a.ready ? '' : '')) + ''; } @@ -26495,6 +26496,22 @@ Be specific and actionable. Format as structured JSON.`; }; window.connectOneLocalAgent = function(id) { connect([id]); }; + window.disconnectLocalAgent = async function(id) { + try { + const data = await api('/api/agents/local/disconnect', { id: id }); + delete pingStatus[id]; + const ids = Array.isArray(data.connected) ? data.connected : connectedIds.filter(function(x){ return x !== id; }); + if (getActiveAgent() === id) setActiveAgentStored(''); + persistConnected(ids); + render(lastDetected, ids); + if (window.toast) toast(id+' disconnected', 'success'); + if (typeof window.refreshSystemStatus === 'function') window.refreshSystemStatus(); + if (typeof updatePreflightChecklist === 'function') { try { updatePreflightChecklist(); } catch(e) {} } + } catch(e) { + if (window.toast) toast('Disconnect failed: '+e.message, 'error'); + } + }; + // Pin (or unpin) which connected agent drives missions + analysis as the LLM backbone. // Clicking the active one again clears the pin (back to the auto codex→hermes→claude order). window.setActiveLocalAgent = function(id) { diff --git a/src/__tests__/local-api-hardening-static.test.ts b/src/__tests__/local-api-hardening-static.test.ts index e256b5a..efee3b1 100644 --- a/src/__tests__/local-api-hardening-static.test.ts +++ b/src/__tests__/local-api-hardening-static.test.ts @@ -108,6 +108,14 @@ describe('local API authorization hardening invariants', () => { expect(uiSource).toMatch(/\['codex', 'mock', 'local', 'local-agent'\]/); }); + it('Settings can disconnect a local agent without silently reconnecting it', () => { + expect(uiSource).toMatch(/onclick="disconnectLocalAgent\(\\'/); + expect(uiSource).toContain("api('/api/agents/local/disconnect', { id: id })"); + expect(uiSource).toContain("if (getActiveAgent() === id) setActiveAgentStored('')"); + expect(uiSource).toContain('persistConnected(ids)'); + expect(uiSource).toContain('delete pingStatus[id]'); + }); + it('Full Auto resumes with the exact approved receipt instead of minting another', () => { const start = uiSource.indexOf('async function generalFullAuto('); const end = uiSource.indexOf('/**\n * REQUEST SITREP', start);