clementine-agent 1.18.81 → 1.18.82
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/agent/assistant.d.ts +15 -0
- package/dist/agent/assistant.js +15 -0
- package/dist/cli/dashboard.js +243 -2
- package/dist/gateway/router.d.ts +9 -0
- package/dist/gateway/router.js +4 -0
- package/package.json +1 -1
|
@@ -100,6 +100,21 @@ export declare class PersonalAssistant {
|
|
|
100
100
|
}>;
|
|
101
101
|
updatedAt: string;
|
|
102
102
|
};
|
|
103
|
+
/**
|
|
104
|
+
* PRD Phase 2.1: clear the cached status for one server so the next query
|
|
105
|
+
* repopulates it from a fresh handshake. The SDK manages connections
|
|
106
|
+
* internally; we don't have a direct "reconnect now" hook, but invalidating
|
|
107
|
+
* the cached entry tells the dashboard to render 'pending' and resets any
|
|
108
|
+
* stale error/auth state. Returns the post-clear cached snapshot.
|
|
109
|
+
*/
|
|
110
|
+
invalidateMcpStatus(serverName: string): {
|
|
111
|
+
servers: Array<{
|
|
112
|
+
name: string;
|
|
113
|
+
status: string;
|
|
114
|
+
}>;
|
|
115
|
+
updatedAt: string;
|
|
116
|
+
cleared: boolean;
|
|
117
|
+
};
|
|
103
118
|
/** Inject a background work result into the session as silent follow-up context. */
|
|
104
119
|
injectPendingContext(sessionKey: string, userPrompt: string, result: string): void;
|
|
105
120
|
private initMemoryStore;
|
package/dist/agent/assistant.js
CHANGED
|
@@ -810,6 +810,21 @@ export class PersonalAssistant {
|
|
|
810
810
|
getMcpStatus() {
|
|
811
811
|
return { servers: this._lastMcpStatus, updatedAt: this._lastMcpStatusTime };
|
|
812
812
|
}
|
|
813
|
+
/**
|
|
814
|
+
* PRD Phase 2.1: clear the cached status for one server so the next query
|
|
815
|
+
* repopulates it from a fresh handshake. The SDK manages connections
|
|
816
|
+
* internally; we don't have a direct "reconnect now" hook, but invalidating
|
|
817
|
+
* the cached entry tells the dashboard to render 'pending' and resets any
|
|
818
|
+
* stale error/auth state. Returns the post-clear cached snapshot.
|
|
819
|
+
*/
|
|
820
|
+
invalidateMcpStatus(serverName) {
|
|
821
|
+
const beforeLen = this._lastMcpStatus.length;
|
|
822
|
+
this._lastMcpStatus = this._lastMcpStatus.filter((s) => s.name !== serverName);
|
|
823
|
+
const cleared = this._lastMcpStatus.length < beforeLen;
|
|
824
|
+
if (cleared)
|
|
825
|
+
this._lastMcpStatusTime = new Date().toISOString();
|
|
826
|
+
return { servers: this._lastMcpStatus, updatedAt: this._lastMcpStatusTime, cleared };
|
|
827
|
+
}
|
|
813
828
|
/** Inject a background work result into the session as silent follow-up context. */
|
|
814
829
|
injectPendingContext(sessionKey, userPrompt, result) {
|
|
815
830
|
const pending = this.pendingContext.get(sessionKey) ?? [];
|
package/dist/cli/dashboard.js
CHANGED
|
@@ -9266,6 +9266,31 @@ If the tool returns nothing or errors, return an empty array \`[]\`.`,
|
|
|
9266
9266
|
app.get('/api/mcp-status', gwHandler(async (gw, _req, res) => {
|
|
9267
9267
|
res.json(gw.getMcpStatus());
|
|
9268
9268
|
}));
|
|
9269
|
+
// PRD Phase 2.1: Reconnect a single MCP server. Clears the cached status so
|
|
9270
|
+
// the next query handshake repopulates it. The SDK doesn't expose a direct
|
|
9271
|
+
// reconnect call, so this is the closest equivalent: kick the cache.
|
|
9272
|
+
app.post('/api/mcp-servers/:name/reconnect', gwHandler(async (gw, req, res) => {
|
|
9273
|
+
const rawName = req.params.name;
|
|
9274
|
+
const name = Array.isArray(rawName) ? rawName[0] : rawName;
|
|
9275
|
+
if (!name) {
|
|
9276
|
+
res.status(400).json({ ok: false, error: 'name required' });
|
|
9277
|
+
return;
|
|
9278
|
+
}
|
|
9279
|
+
try {
|
|
9280
|
+
const result = gw.invalidateMcpStatus(String(name));
|
|
9281
|
+
res.json({
|
|
9282
|
+
ok: true,
|
|
9283
|
+
cleared: result.cleared,
|
|
9284
|
+
message: result.cleared
|
|
9285
|
+
? `Reconnect queued for "${name}" — status will refresh on the next query.`
|
|
9286
|
+
: `"${name}" had no cached status to clear; next query will populate it.`,
|
|
9287
|
+
status: result,
|
|
9288
|
+
});
|
|
9289
|
+
}
|
|
9290
|
+
catch (err) {
|
|
9291
|
+
res.status(500).json({ ok: false, error: String(err) });
|
|
9292
|
+
}
|
|
9293
|
+
}));
|
|
9269
9294
|
// ── Self-Improvement API ─────────────────────────────────────────
|
|
9270
9295
|
// ── MCP Server Management API ───────────────────────────────────────
|
|
9271
9296
|
app.get('/api/mcp-servers', (_req, res) => {
|
|
@@ -20319,6 +20344,77 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20319
20344
|
|
|
20320
20345
|
<!-- (legacy standalone Preview modal removed in 1.18.70 — preview now lives as a tab inside the cron modal) -->
|
|
20321
20346
|
|
|
20347
|
+
<!-- ═══ MCP Server Edit Modal — PRD Phase 2.1 ═══ -->
|
|
20348
|
+
<div class="modal-overlay" id="mcp-edit-modal">
|
|
20349
|
+
<div class="modal" style="max-width:640px;width:96vw">
|
|
20350
|
+
<div class="modal-header">
|
|
20351
|
+
<h3 id="mcp-edit-title">Edit MCP Server</h3>
|
|
20352
|
+
<button class="btn-ghost btn-sm" onclick="closeMcpServerEditModal()">×</button>
|
|
20353
|
+
</div>
|
|
20354
|
+
<div class="modal-body" style="padding:18px">
|
|
20355
|
+
<div id="mcp-edit-readonly-note" style="display:none;margin-bottom:14px;padding:10px 12px;border-radius:6px;background:rgba(245,158,11,0.10);border:1px solid rgba(245,158,11,0.30);color:var(--yellow);font-size:12px">
|
|
20356
|
+
⚠ Auto-detected server. Edits to this config aren't persisted by Clementine — change it in the source file (Claude Desktop config, Claude Code settings, or extensions). Use the toggle on the catalog card to enable/disable.
|
|
20357
|
+
</div>
|
|
20358
|
+
<div class="form-group">
|
|
20359
|
+
<label class="form-label">Name</label>
|
|
20360
|
+
<input type="text" id="mcp-edit-name">
|
|
20361
|
+
<div class="form-hint">Identifier — cannot be renamed via this dialog.</div>
|
|
20362
|
+
</div>
|
|
20363
|
+
<div class="form-row">
|
|
20364
|
+
<div class="form-group">
|
|
20365
|
+
<label class="form-label">Transport</label>
|
|
20366
|
+
<select id="mcp-edit-type" onchange="syncMcpEditTransportRows()">
|
|
20367
|
+
<option value="stdio">stdio (local process)</option>
|
|
20368
|
+
<option value="http">http</option>
|
|
20369
|
+
<option value="sse">sse</option>
|
|
20370
|
+
</select>
|
|
20371
|
+
</div>
|
|
20372
|
+
<div class="form-group">
|
|
20373
|
+
<label class="form-label" style="display:flex;align-items:center;gap:8px">
|
|
20374
|
+
<input type="checkbox" id="mcp-edit-enabled"> Enabled
|
|
20375
|
+
</label>
|
|
20376
|
+
<div class="form-hint">Disable to remove this server from every task without deleting it.</div>
|
|
20377
|
+
</div>
|
|
20378
|
+
</div>
|
|
20379
|
+
<div class="form-group">
|
|
20380
|
+
<label class="form-label">Description</label>
|
|
20381
|
+
<input type="text" id="mcp-edit-description" placeholder="What this server does">
|
|
20382
|
+
</div>
|
|
20383
|
+
<!-- stdio-only fields -->
|
|
20384
|
+
<div id="mcp-edit-stdio-rows">
|
|
20385
|
+
<div class="form-group">
|
|
20386
|
+
<label class="form-label">Command</label>
|
|
20387
|
+
<input type="text" id="mcp-edit-command" placeholder="e.g. npx, python, ./bin/server">
|
|
20388
|
+
</div>
|
|
20389
|
+
<div class="form-group">
|
|
20390
|
+
<label class="form-label">Args <span style="color:var(--text-muted);font-weight:normal">(one per line)</span></label>
|
|
20391
|
+
<textarea id="mcp-edit-args" rows="3" placeholder="--port 3001" style="font-family:'JetBrains Mono',monospace;font-size:11px"></textarea>
|
|
20392
|
+
</div>
|
|
20393
|
+
<div class="form-group">
|
|
20394
|
+
<label class="form-label">Env <span style="color:var(--text-muted);font-weight:normal">(JSON object, optional)</span></label>
|
|
20395
|
+
<textarea id="mcp-edit-env" rows="3" placeholder='{ "API_KEY": "..." }' style="font-family:'JetBrains Mono',monospace;font-size:11px"></textarea>
|
|
20396
|
+
</div>
|
|
20397
|
+
</div>
|
|
20398
|
+
<!-- http/sse fields -->
|
|
20399
|
+
<div id="mcp-edit-http-rows" style="display:none">
|
|
20400
|
+
<div class="form-group">
|
|
20401
|
+
<label class="form-label">URL</label>
|
|
20402
|
+
<input type="text" id="mcp-edit-url" placeholder="https://example.com/mcp">
|
|
20403
|
+
</div>
|
|
20404
|
+
<div class="form-group">
|
|
20405
|
+
<label class="form-label">Headers <span style="color:var(--text-muted);font-weight:normal">(JSON object, optional)</span></label>
|
|
20406
|
+
<textarea id="mcp-edit-headers" rows="3" placeholder='{ "Authorization": "Bearer ..." }' style="font-family:'JetBrains Mono',monospace;font-size:11px"></textarea>
|
|
20407
|
+
</div>
|
|
20408
|
+
</div>
|
|
20409
|
+
</div>
|
|
20410
|
+
<div class="modal-footer">
|
|
20411
|
+
<span style="flex:1"></span>
|
|
20412
|
+
<button onclick="closeMcpServerEditModal()">Close</button>
|
|
20413
|
+
<button class="btn-primary" id="mcp-edit-save" onclick="saveMcpServerEdit()">Save</button>
|
|
20414
|
+
</div>
|
|
20415
|
+
</div>
|
|
20416
|
+
</div>
|
|
20417
|
+
|
|
20322
20418
|
<!-- ═══ Goal Modal ═══ -->
|
|
20323
20419
|
<div class="modal-overlay" id="goal-modal">
|
|
20324
20420
|
<div class="modal" style="width:520px">
|
|
@@ -23677,14 +23773,159 @@ function renderMcpCatalogCard(server, statusMap) {
|
|
|
23677
23773
|
+ (server.description ? '<div style="font-size:12px;color:var(--text-secondary);line-height:1.45">' + esc(String(server.description).slice(0, 240)) + '</div>' : '')
|
|
23678
23774
|
+ (lastError ? '<div style="font-size:11px;color:var(--red);background:rgba(239,68,68,0.06);padding:6px 8px;border-radius:4px;word-break:break-word">' + esc(String(lastError).slice(0, 200)) + '</div>' : '')
|
|
23679
23775
|
+ (lastChecked ? '<div style="font-size:11px;color:var(--text-muted)">Checked ' + esc(timeAgo(lastChecked)) + '</div>' : '')
|
|
23680
|
-
+ '<div style="display:flex;gap:6px;margin-top:4px">'
|
|
23776
|
+
+ '<div style="display:flex;gap:6px;margin-top:4px;flex-wrap:wrap">'
|
|
23681
23777
|
+ '<button class="btn-sm" onclick="toggleMcpServerEnabled(\\x27' + jsStr(name) + '\\x27,' + (enabled ? 'false' : 'true') + ')" title="' + (enabled ? 'Disable this MCP server for all tasks' : 'Enable this MCP server') + '">' + (enabled ? 'Disable' : 'Enable') + '</button>'
|
|
23682
|
-
+ '<button class="btn-sm"
|
|
23778
|
+
+ '<button class="btn-sm" onclick="reconnectMcpServer(\\x27' + jsStr(name) + '\\x27)" title="Clear cached status — next query will reconnect">Reconnect</button>'
|
|
23779
|
+
+ '<button class="btn-sm" onclick="openMcpServerEditModal(\\x27' + jsStr(name) + '\\x27)" title="View or edit this server\\x27s config">Edit</button>'
|
|
23683
23780
|
+ '</div>'
|
|
23684
23781
|
+ '</div>';
|
|
23685
23782
|
return html;
|
|
23686
23783
|
}
|
|
23687
23784
|
|
|
23785
|
+
// PRD Phase 2.1: Reconnect — invalidate cached status server-side, refresh
|
|
23786
|
+
// the catalog so the user sees the pending pill until the next query
|
|
23787
|
+
// handshake repopulates it.
|
|
23788
|
+
async function reconnectMcpServer(name) {
|
|
23789
|
+
try {
|
|
23790
|
+
var r = await apiFetch('/api/mcp-servers/' + encodeURIComponent(name) + '/reconnect', { method: 'POST' });
|
|
23791
|
+
var d = await r.json();
|
|
23792
|
+
if (!r.ok || d.ok === false) {
|
|
23793
|
+
toast('Reconnect failed: ' + (d.error || 'unknown'), 'error');
|
|
23794
|
+
return;
|
|
23795
|
+
}
|
|
23796
|
+
toast(d.message || 'Reconnect queued.', 'info');
|
|
23797
|
+
refreshToolsMcpCatalog();
|
|
23798
|
+
} catch (e) {
|
|
23799
|
+
toast('Reconnect failed: ' + String(e), 'error');
|
|
23800
|
+
}
|
|
23801
|
+
}
|
|
23802
|
+
|
|
23803
|
+
// PRD Phase 2.1: Edit modal. User-managed servers get an editable config
|
|
23804
|
+
// form; auto-detected servers render the same fields read-only with a note
|
|
23805
|
+
// pointing the user at the underlying config file.
|
|
23806
|
+
async function openMcpServerEditModal(name) {
|
|
23807
|
+
// Pull the latest server config — don't trust whatever was on the rendered card.
|
|
23808
|
+
var server;
|
|
23809
|
+
try {
|
|
23810
|
+
var r = await apiFetch('/api/mcp-servers');
|
|
23811
|
+
var d = await r.json();
|
|
23812
|
+
server = (d && d.servers || []).find(function(s) { return s.name === name; });
|
|
23813
|
+
} catch (e) {
|
|
23814
|
+
toast('Failed to load server: ' + String(e), 'error');
|
|
23815
|
+
return;
|
|
23816
|
+
}
|
|
23817
|
+
if (!server) { toast('Server "' + name + '" not found', 'error'); return; }
|
|
23818
|
+
var modal = document.getElementById('mcp-edit-modal');
|
|
23819
|
+
if (!modal) { toast('Edit modal missing from DOM', 'error'); return; }
|
|
23820
|
+
document.getElementById('mcp-edit-title').textContent = 'Edit: ' + name;
|
|
23821
|
+
var roNote = document.getElementById('mcp-edit-readonly-note');
|
|
23822
|
+
var isReadOnly = server.source === 'auto-detected';
|
|
23823
|
+
if (roNote) roNote.style.display = isReadOnly ? '' : 'none';
|
|
23824
|
+
// Set fields
|
|
23825
|
+
document.getElementById('mcp-edit-name').value = server.name || '';
|
|
23826
|
+
document.getElementById('mcp-edit-name').disabled = true; // never rename via this path
|
|
23827
|
+
document.getElementById('mcp-edit-type').value = server.type || 'stdio';
|
|
23828
|
+
document.getElementById('mcp-edit-type').disabled = isReadOnly;
|
|
23829
|
+
document.getElementById('mcp-edit-description').value = server.description || '';
|
|
23830
|
+
document.getElementById('mcp-edit-description').disabled = isReadOnly;
|
|
23831
|
+
document.getElementById('mcp-edit-enabled').checked = server.enabled !== false;
|
|
23832
|
+
document.getElementById('mcp-edit-enabled').disabled = isReadOnly;
|
|
23833
|
+
document.getElementById('mcp-edit-command').value = server.command || '';
|
|
23834
|
+
document.getElementById('mcp-edit-command').disabled = isReadOnly;
|
|
23835
|
+
document.getElementById('mcp-edit-args').value = Array.isArray(server.args) ? server.args.join('\\n') : '';
|
|
23836
|
+
document.getElementById('mcp-edit-args').disabled = isReadOnly;
|
|
23837
|
+
document.getElementById('mcp-edit-url').value = server.url || '';
|
|
23838
|
+
document.getElementById('mcp-edit-url').disabled = isReadOnly;
|
|
23839
|
+
document.getElementById('mcp-edit-headers').value = server.headers && Object.keys(server.headers).length ? JSON.stringify(server.headers, null, 2) : '';
|
|
23840
|
+
document.getElementById('mcp-edit-headers').disabled = isReadOnly;
|
|
23841
|
+
document.getElementById('mcp-edit-env').value = server.env && Object.keys(server.env).length ? JSON.stringify(server.env, null, 2) : '';
|
|
23842
|
+
document.getElementById('mcp-edit-env').disabled = isReadOnly;
|
|
23843
|
+
// Show the right transport fields
|
|
23844
|
+
syncMcpEditTransportRows();
|
|
23845
|
+
// Save button hidden for read-only auto-detected servers.
|
|
23846
|
+
var saveBtn = document.getElementById('mcp-edit-save');
|
|
23847
|
+
if (saveBtn) saveBtn.style.display = isReadOnly ? 'none' : '';
|
|
23848
|
+
modal.classList.add('show');
|
|
23849
|
+
}
|
|
23850
|
+
|
|
23851
|
+
function closeMcpServerEditModal() {
|
|
23852
|
+
var modal = document.getElementById('mcp-edit-modal');
|
|
23853
|
+
if (modal) modal.classList.remove('show');
|
|
23854
|
+
}
|
|
23855
|
+
|
|
23856
|
+
// Show only the row matching the selected transport (stdio vs http/sse).
|
|
23857
|
+
function syncMcpEditTransportRows() {
|
|
23858
|
+
var t = (document.getElementById('mcp-edit-type') || {}).value || 'stdio';
|
|
23859
|
+
var stdioRow = document.getElementById('mcp-edit-stdio-rows');
|
|
23860
|
+
var httpRow = document.getElementById('mcp-edit-http-rows');
|
|
23861
|
+
if (stdioRow) stdioRow.style.display = (t === 'stdio') ? '' : 'none';
|
|
23862
|
+
if (httpRow) httpRow.style.display = (t === 'http' || t === 'sse') ? '' : 'none';
|
|
23863
|
+
}
|
|
23864
|
+
|
|
23865
|
+
async function saveMcpServerEdit() {
|
|
23866
|
+
var name = (document.getElementById('mcp-edit-name') || {}).value;
|
|
23867
|
+
if (!name) return;
|
|
23868
|
+
var type = document.getElementById('mcp-edit-type').value;
|
|
23869
|
+
var description = document.getElementById('mcp-edit-description').value.trim();
|
|
23870
|
+
var enabled = !!document.getElementById('mcp-edit-enabled').checked;
|
|
23871
|
+
var commandRaw = document.getElementById('mcp-edit-command').value.trim();
|
|
23872
|
+
var argsRaw = document.getElementById('mcp-edit-args').value;
|
|
23873
|
+
var url = document.getElementById('mcp-edit-url').value.trim();
|
|
23874
|
+
var headersRaw = document.getElementById('mcp-edit-headers').value.trim();
|
|
23875
|
+
var envRaw = document.getElementById('mcp-edit-env').value.trim();
|
|
23876
|
+
|
|
23877
|
+
var headers, env;
|
|
23878
|
+
if (headersRaw) {
|
|
23879
|
+
try {
|
|
23880
|
+
headers = JSON.parse(headersRaw);
|
|
23881
|
+
if (!headers || typeof headers !== 'object' || Array.isArray(headers)) throw new Error('Headers must be a JSON object');
|
|
23882
|
+
} catch (e) {
|
|
23883
|
+
toast('Headers JSON invalid: ' + (e.message || String(e)), 'error');
|
|
23884
|
+
document.getElementById('mcp-edit-headers').focus();
|
|
23885
|
+
return;
|
|
23886
|
+
}
|
|
23887
|
+
}
|
|
23888
|
+
if (envRaw) {
|
|
23889
|
+
try {
|
|
23890
|
+
env = JSON.parse(envRaw);
|
|
23891
|
+
if (!env || typeof env !== 'object' || Array.isArray(env)) throw new Error('Env must be a JSON object');
|
|
23892
|
+
} catch (e) {
|
|
23893
|
+
toast('Env JSON invalid: ' + (e.message || String(e)), 'error');
|
|
23894
|
+
document.getElementById('mcp-edit-env').focus();
|
|
23895
|
+
return;
|
|
23896
|
+
}
|
|
23897
|
+
}
|
|
23898
|
+
var args = argsRaw.split(/\\r?\\n/).map(function(s){ return s.trim(); }).filter(Boolean);
|
|
23899
|
+
var body = { type: type, description: description, enabled: enabled };
|
|
23900
|
+
if (type === 'stdio') {
|
|
23901
|
+
if (!commandRaw) { toast('Command is required for stdio transport', 'error'); document.getElementById('mcp-edit-command').focus(); return; }
|
|
23902
|
+
body.command = commandRaw;
|
|
23903
|
+
body.args = args;
|
|
23904
|
+
if (env) body.env = env;
|
|
23905
|
+
} else {
|
|
23906
|
+
if (!url) { toast('URL is required for ' + type + ' transport', 'error'); document.getElementById('mcp-edit-url').focus(); return; }
|
|
23907
|
+
body.url = url;
|
|
23908
|
+
if (headers) body.headers = headers;
|
|
23909
|
+
}
|
|
23910
|
+
try {
|
|
23911
|
+
var r = await apiFetch('/api/mcp-servers/' + encodeURIComponent(name), {
|
|
23912
|
+
method: 'PUT',
|
|
23913
|
+
headers: { 'Content-Type': 'application/json' },
|
|
23914
|
+
body: JSON.stringify(body),
|
|
23915
|
+
});
|
|
23916
|
+
var d = await r.json();
|
|
23917
|
+
if (!r.ok || d.error) {
|
|
23918
|
+
toast('Save failed: ' + (d.error || 'unknown'), 'error');
|
|
23919
|
+
return;
|
|
23920
|
+
}
|
|
23921
|
+
toast('Saved.', 'success');
|
|
23922
|
+
closeMcpServerEditModal();
|
|
23923
|
+
refreshToolsMcpCatalog();
|
|
23924
|
+
} catch (e) {
|
|
23925
|
+
toast('Save failed: ' + String(e), 'error');
|
|
23926
|
+
}
|
|
23927
|
+
}
|
|
23928
|
+
|
|
23688
23929
|
// PUT helper for the Toggle button. Lazy: re-fetches the catalog after
|
|
23689
23930
|
// the round-trip so the new state is reflected. Future slice will swap
|
|
23690
23931
|
// to optimistic update + rollback on error.
|
package/dist/gateway/router.d.ts
CHANGED
|
@@ -255,6 +255,15 @@ export declare class Gateway {
|
|
|
255
255
|
}>;
|
|
256
256
|
updatedAt: string;
|
|
257
257
|
};
|
|
258
|
+
/** PRD Phase 2.1: thin pass-through for the dashboard's Reconnect button. */
|
|
259
|
+
invalidateMcpStatus(serverName: string): {
|
|
260
|
+
servers: Array<{
|
|
261
|
+
name: string;
|
|
262
|
+
status: string;
|
|
263
|
+
}>;
|
|
264
|
+
updatedAt: string;
|
|
265
|
+
cleared: boolean;
|
|
266
|
+
};
|
|
258
267
|
getPresenceInfo(sessionKey: string): {
|
|
259
268
|
model: string;
|
|
260
269
|
project: string | null;
|
package/dist/gateway/router.js
CHANGED
|
@@ -2241,6 +2241,10 @@ export class Gateway {
|
|
|
2241
2241
|
getMcpStatus() {
|
|
2242
2242
|
return this.assistant.getMcpStatus();
|
|
2243
2243
|
}
|
|
2244
|
+
/** PRD Phase 2.1: thin pass-through for the dashboard's Reconnect button. */
|
|
2245
|
+
invalidateMcpStatus(serverName) {
|
|
2246
|
+
return this.assistant.invalidateMcpStatus(serverName);
|
|
2247
|
+
}
|
|
2244
2248
|
getPresenceInfo(sessionKey) {
|
|
2245
2249
|
const sess = this.sessions.get(sessionKey);
|
|
2246
2250
|
const modelName = sess?.model
|