clementine-agent 1.18.80 → 1.18.81

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.
@@ -16356,14 +16356,18 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
16356
16356
 
16357
16357
  <!-- ═══ Tasks Page — single unified surface ═══ -->
16358
16358
  <div class="page" id="page-build">
16359
- <!-- Sub-tabs hidden by default. Cron is the single surface; multi-step
16360
- workflows (formerly "Tricks") are still accessible via deep-link
16361
- ?tab=workflows or by toggling .show-workflows-tabs on the page. -->
16362
- <div id="build-tabs" style="display:none;gap:4px;padding:8px 18px 0;background:var(--bg-secondary);border-bottom:1px solid var(--border);flex-shrink:0">
16359
+ <!-- PRD Phase 2: top-level tab strip within the Tasks domain.
16360
+ Tasks (default) + Tools & MCP catalog. Workflows still reachable
16361
+ via deep-link ?tab=workflows for power users with existing
16362
+ multi-step workflows. -->
16363
+ <div id="build-tabs" style="display:flex;gap:4px;padding:8px 18px 0;background:var(--bg-secondary);border-bottom:1px solid var(--border);flex-shrink:0">
16363
16364
  <button class="build-tab-btn active" data-build-tab="crons" onclick="switchBuildTab('crons')" style="padding:8px 14px;border-radius:6px 6px 0 0;border:none;background:transparent;color:var(--text-primary);font-size:13px;font-weight:500;cursor:pointer;border-bottom:2px solid transparent">
16364
16365
  <span style="margin-right:6px">📅</span>Tasks <span id="build-tab-cron-count" style="display:none;margin-left:4px;font-size:10px;background:var(--bg-tertiary);padding:1px 6px;border-radius:999px;color:var(--text-muted)">0</span>
16365
16366
  </button>
16366
- <button class="build-tab-btn" data-build-tab="workflows" onclick="switchBuildTab('workflows')" style="padding:8px 14px;border-radius:6px 6px 0 0;border:none;background:transparent;color:var(--text-secondary);font-size:13px;font-weight:500;cursor:pointer;border-bottom:2px solid transparent">
16367
+ <button class="build-tab-btn" data-build-tab="toolsmcp" onclick="switchBuildTab('toolsmcp')" style="padding:8px 14px;border-radius:6px 6px 0 0;border:none;background:transparent;color:var(--text-secondary);font-size:13px;font-weight:500;cursor:pointer;border-bottom:2px solid transparent">
16368
+ <span style="margin-right:6px">🧰</span>Tools &amp; MCP <span id="build-tab-toolsmcp-count" style="display:none;margin-left:4px;font-size:10px;background:var(--bg-tertiary);padding:1px 6px;border-radius:999px;color:var(--text-muted)">0</span>
16369
+ </button>
16370
+ <button class="build-tab-btn" data-build-tab="workflows" onclick="switchBuildTab('workflows')" style="display:none;padding:8px 14px;border-radius:6px 6px 0 0;border:none;background:transparent;color:var(--text-secondary);font-size:13px;font-weight:500;cursor:pointer;border-bottom:2px solid transparent">
16367
16371
  <span style="margin-right:6px">🔧</span>Workflows <span id="build-tab-workflows-count" style="display:none;margin-left:4px;font-size:10px;background:var(--bg-tertiary);padding:1px 6px;border-radius:999px;color:var(--text-muted)">0</span>
16368
16372
  </button>
16369
16373
  </div>
@@ -16375,6 +16379,12 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
16375
16379
  <div id="build-tab-crons" style="display:none;flex:1;min-height:0;overflow-y:auto;padding:18px;background:var(--bg-primary)">
16376
16380
  <div id="panel-cron"><div class="empty-state" style="padding:24px;color:var(--text-muted)">Loading scheduled tasks…</div></div>
16377
16381
  </div>
16382
+ <!-- ── PRD Phase 2: Tools & MCP catalog ────────────────────────────────
16383
+ Read-only foundation in 1.18.81. Future slices: per-tool bindings,
16384
+ Reconnect/Toggle/Edit actions, Approval Mode + Max-auto-runs config. -->
16385
+ <div id="build-tab-toolsmcp" style="display:none;flex:1;min-height:0;overflow-y:auto;padding:18px;background:var(--bg-primary)">
16386
+ <div id="panel-toolsmcp"><div class="empty-state" style="padding:24px;color:var(--text-muted)">Loading Tools &amp; MCP catalog…</div></div>
16387
+ </div>
16378
16388
  <!-- Tricks (workflows) tab — existing RoutinesUI surface ─────────────────── -->
16379
16389
  <div id="build-tab-workflows" style="display:none;flex:1;min-height:0;display:flex;flex-direction:column">
16380
16390
  <!-- Toolbar -->
@@ -21148,12 +21158,27 @@ function switchBuildTab(tab) {
21148
21158
  var workPane = document.getElementById('build-tab-workflows');
21149
21159
  var cronPane = document.getElementById('build-tab-crons');
21150
21160
  var tplPane = document.getElementById('build-tab-templates');
21161
+ var toolsmcpPane = document.getElementById('build-tab-toolsmcp');
21151
21162
  var headerStrip = document.getElementById('build-header-strip');
21152
21163
  var usagePanel = document.getElementById('build-usage-panel');
21153
21164
  var newBtn = document.getElementById('builder-new-btn');
21154
21165
  // Always close any open workflow when changing tabs — switching context
21155
21166
  // is a clean slate, not a stale node hanging on the canvas.
21156
21167
  if (typeof closeBuilderCanvas === 'function') closeBuilderCanvas();
21168
+ // Default: hide the Tools & MCP pane unless we're explicitly on it.
21169
+ if (toolsmcpPane && tab !== 'toolsmcp') toolsmcpPane.style.display = 'none';
21170
+ if (tab === 'toolsmcp') {
21171
+ // PRD Phase 2: Tools & MCP catalog. Read-only foundation in 1.18.81.
21172
+ if (workPane) workPane.style.display = 'none';
21173
+ if (cronPane) cronPane.style.display = 'none';
21174
+ if (tplPane) tplPane.style.display = 'none';
21175
+ if (toolsmcpPane) toolsmcpPane.style.display = 'block';
21176
+ if (headerStrip) headerStrip.style.display = 'none';
21177
+ if (usagePanel) usagePanel.style.display = 'none';
21178
+ if (newBtn) newBtn.style.display = 'none';
21179
+ if (typeof refreshToolsMcpCatalog === 'function') refreshToolsMcpCatalog();
21180
+ return;
21181
+ }
21157
21182
  if (tab === 'templates') {
21158
21183
  if (workPane) workPane.style.display = 'none';
21159
21184
  if (cronPane) cronPane.style.display = 'none';
@@ -23532,6 +23557,156 @@ function renderRunningCard(item) {
23532
23557
  + '</div></div>';
23533
23558
  }
23534
23559
 
23560
+ // ── PRD Phase 2: Tools & MCP catalog ──────────────────────────────────
23561
+ // Read-only foundation in 1.18.81. Renders the four-card taxonomy:
23562
+ // • Built-in — Claude SDK native tools (Read/Write/Bash/etc.)
23563
+ // • Custom MCP — in-process SDK MCP servers
23564
+ // • Shell command — CLI wrappers
23565
+ // • External MCP — stdio / sse / http MCP servers
23566
+ // Pulls from /api/mcp-status (live status) + /api/mcp-servers (config).
23567
+ // Future slices wire Reconnect/Toggle/Edit + the McpToolBinding modal.
23568
+ async function refreshToolsMcpCatalog() {
23569
+ var panel = document.getElementById('panel-toolsmcp');
23570
+ if (!panel) return;
23571
+ panel.innerHTML = '<div class="empty-state" style="padding:24px;color:var(--text-muted)">Loading Tools &amp; MCP catalog…</div>';
23572
+ var statusMap = {};
23573
+ var servers = [];
23574
+ try {
23575
+ var sR = await apiFetch('/api/mcp-status');
23576
+ var statusJson = await sR.json();
23577
+ statusMap = statusJson || {};
23578
+ } catch (e) { /* status is optional — servers still render without it */ }
23579
+ try {
23580
+ var lR = await apiFetch('/api/mcp-servers');
23581
+ var lJson = await lR.json();
23582
+ servers = (lJson && lJson.servers) || [];
23583
+ } catch (e) {
23584
+ panel.innerHTML = '<div class="empty-state" style="padding:24px;color:var(--red)">Failed to load MCP servers: ' + esc(String(e)) + '</div>';
23585
+ return;
23586
+ }
23587
+ var tabCount = document.getElementById('build-tab-toolsmcp-count');
23588
+ if (tabCount) {
23589
+ tabCount.textContent = servers.length;
23590
+ tabCount.style.display = servers.length > 0 ? '' : 'none';
23591
+ }
23592
+ // Bucket servers into the four PRD categories. The existing
23593
+ // ManagedMcpServer type doesn't have an explicit "kind" field, so we
23594
+ // infer: stdio with a known shell binary → 'shell', stdio bundled with
23595
+ // clementine → 'builtin', stdio external command → 'external_stdio',
23596
+ // http/sse → 'external_remote'. The bucket keys map to the PRD's four
23597
+ // taxonomy cards.
23598
+ var buckets = { builtin: [], custom: [], shell: [], external: [] };
23599
+ for (var i = 0; i < servers.length; i++) {
23600
+ var s = servers[i];
23601
+ var name = s.name || '';
23602
+ var type = s.type || 'stdio';
23603
+ var cmd = s.command || '';
23604
+ var kind;
23605
+ // The clementine-tools server is an in-process bundle
23606
+ if (name === 'clementine-tools' || name === 'kernel') kind = 'builtin';
23607
+ else if (type === 'http' || type === 'sse') kind = 'external';
23608
+ else if (/^(sf|gh|gcloud|kubectl|docker|aws|az|terraform)$/.test(cmd) || /\\b(sf|gh|gcloud|kubectl)$/.test(cmd)) kind = 'shell';
23609
+ else kind = 'external'; // default for stdio external MCP
23610
+ buckets[kind].push(s);
23611
+ }
23612
+ var html = '';
23613
+ // Header strip
23614
+ html += '<div style="margin-bottom:18px"><h2 style="margin:0 0 4px;font-size:18px;font-weight:600;color:var(--text-primary)">Tools &amp; MCP catalog</h2>'
23615
+ + '<div style="font-size:12px;color:var(--text-muted)">'+ esc(servers.length) +' MCP server' + (servers.length === 1 ? '' : 's') + ' configured. Click any task in the Tasks tab to bind specific tools to that task.</div></div>';
23616
+ // Four-card taxonomy. Each section is a labeled bucket of cards.
23617
+ var sections = [
23618
+ { key: 'builtin', label: 'Built-in', desc: 'Claude SDK native tools — always available to every task at the agent profile\\x27s permission tier.' },
23619
+ { key: 'custom', label: 'Custom in-process MCP', desc: 'MCP servers defined in clementine\\x27s code, loaded inside the daemon process.' },
23620
+ { key: 'shell', label: 'Shell commands', desc: 'Local CLI binaries (sf, gh, gcloud…) wrapped as MCP servers.' },
23621
+ { key: 'external', label: 'External MCP servers', desc: 'Third-party MCP servers reached over stdio, SSE, or HTTP.' },
23622
+ ];
23623
+ for (var k = 0; k < sections.length; k++) {
23624
+ var sec = sections[k];
23625
+ var bucket = buckets[sec.key] || [];
23626
+ html += '<div style="margin-bottom:24px">';
23627
+ html += '<div style="display:flex;align-items:baseline;gap:10px;margin-bottom:10px">'
23628
+ + '<h3 style="margin:0;font-size:14px;font-weight:600;color:var(--text-primary)">' + esc(sec.label) + '</h3>'
23629
+ + '<span style="font-size:11px;color:var(--text-muted);font-weight:500">' + bucket.length + '</span>'
23630
+ + '</div>';
23631
+ html += '<div style="font-size:11px;color:var(--text-muted);margin-bottom:12px">' + esc(sec.desc) + '</div>';
23632
+ if (bucket.length === 0) {
23633
+ html += '<div class="empty-state" style="padding:14px;color:var(--text-muted);font-size:12px;background:var(--bg-secondary);border:1px dashed var(--border);border-radius:6px">No servers in this bucket.</div>';
23634
+ } else {
23635
+ html += '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px">';
23636
+ for (var b = 0; b < bucket.length; b++) html += renderMcpCatalogCard(bucket[b], statusMap);
23637
+ html += '</div>';
23638
+ }
23639
+ html += '</div>';
23640
+ }
23641
+ panel.innerHTML = html;
23642
+ }
23643
+
23644
+ // Render one MCP server card. Status pill colors mirror the PRD's five
23645
+ // states (connected / failed / needs-auth / pending / disabled). The
23646
+ // statusMap shape comes from gw.getMcpStatus() — varies a bit between
23647
+ // SDK versions; we defensively probe for connected/healthy fields.
23648
+ function renderMcpCatalogCard(server, statusMap) {
23649
+ var name = server.name || '(unnamed)';
23650
+ var transport = server.type || 'stdio';
23651
+ var enabled = server.enabled !== false;
23652
+ var status = statusMap && statusMap[name];
23653
+ var statusKind, statusLabel, statusColor;
23654
+ if (!enabled) { statusKind = 'disabled'; statusLabel = 'disabled'; statusColor = 'var(--text-muted)'; }
23655
+ else if (status && (status.connected === true || status.status === 'connected' || status.healthy === true)) { statusKind = 'connected'; statusLabel = 'connected'; statusColor = 'var(--green)'; }
23656
+ else if (status && (status.needsAuth === true || status.status === 'needs-auth')) { statusKind = 'needsauth'; statusLabel = 'needs auth'; statusColor = 'var(--yellow)'; }
23657
+ else if (status && (status.connected === false || status.status === 'failed' || status.error)) { statusKind = 'failed'; statusLabel = 'failed'; statusColor = 'var(--red)'; }
23658
+ else { statusKind = 'pending'; statusLabel = 'pending'; statusColor = 'var(--text-muted)'; }
23659
+ var toolCount = (status && (status.toolCount != null ? status.toolCount : (Array.isArray(status.tools) ? status.tools.length : null))) || (Array.isArray(server.exposedTools) ? server.exposedTools.length : null);
23660
+ var lastChecked = status && (status.lastCheckedAt || status.checkedAt || status.updatedAt);
23661
+ var src = server.source === 'auto-detected' ? 'auto-detected' : 'user-configured';
23662
+ var lastError = status && status.error;
23663
+ var html = ''
23664
+ + '<div style="background:var(--bg-secondary);border:1px solid var(--border);border-radius:8px;padding:14px;display:flex;flex-direction:column;gap:8px">'
23665
+ + '<div style="display:flex;align-items:center;gap:8px">'
23666
+ + '<div style="font-size:13px;font-weight:600;color:var(--text-primary);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + esc(name) + '">' + esc(name) + '</div>'
23667
+ + '<span style="display:inline-flex;align-items:center;gap:5px;font-size:10px;font-weight:500;color:' + statusColor + ';background:' + statusColor + '20;padding:2px 8px;border-radius:999px;text-transform:uppercase;letter-spacing:0.04em">'
23668
+ + '<span style="display:inline-block;width:6px;height:6px;border-radius:50%;background:' + statusColor + '"></span>' + esc(statusLabel)
23669
+ + '</span>'
23670
+ + '</div>'
23671
+ + '<div style="display:flex;align-items:center;gap:10px;font-size:11px;color:var(--text-muted)">'
23672
+ + '<span style="text-transform:uppercase;letter-spacing:0.04em">' + esc(transport) + '</span>'
23673
+ + '<span>·</span>'
23674
+ + '<span>' + esc(src) + '</span>'
23675
+ + (toolCount != null ? '<span>·</span><span>' + esc(toolCount) + ' tool' + (toolCount === 1 ? '' : 's') + '</span>' : '')
23676
+ + '</div>'
23677
+ + (server.description ? '<div style="font-size:12px;color:var(--text-secondary);line-height:1.45">' + esc(String(server.description).slice(0, 240)) + '</div>' : '')
23678
+ + (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
+ + (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">'
23681
+ + '<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" disabled title="Edit + Reconnect coming in the next slice" style="opacity:0.55;cursor:not-allowed">Edit</button>'
23683
+ + '</div>'
23684
+ + '</div>';
23685
+ return html;
23686
+ }
23687
+
23688
+ // PUT helper for the Toggle button. Lazy: re-fetches the catalog after
23689
+ // the round-trip so the new state is reflected. Future slice will swap
23690
+ // to optimistic update + rollback on error.
23691
+ async function toggleMcpServerEnabled(name, nextEnabled) {
23692
+ try {
23693
+ var r = await apiFetch('/api/mcp-servers/' + encodeURIComponent(name), {
23694
+ method: 'PUT',
23695
+ headers: { 'Content-Type': 'application/json' },
23696
+ body: JSON.stringify({ enabled: nextEnabled }),
23697
+ });
23698
+ var d = await r.json();
23699
+ if (!r.ok || d.error) {
23700
+ toast('Toggle failed: ' + (d.error || 'unknown'), 'error');
23701
+ return;
23702
+ }
23703
+ toast(name + ' is now ' + (nextEnabled ? 'enabled' : 'disabled'), 'success');
23704
+ refreshToolsMcpCatalog();
23705
+ } catch (e) {
23706
+ toast('Toggle failed: ' + String(e), 'error');
23707
+ }
23708
+ }
23709
+
23535
23710
  async function refreshCron() {
23536
23711
  try {
23537
23712
  // Fetch operations + cross-job recent runs in parallel for the three-zone
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.18.80",
3
+ "version": "1.18.81",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",