@minasoft/mina-ai-router 0.1.5 → 0.2.1
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 +69 -16
- package/dist/apps/cli/src/index.js +1251 -46
- package/dist/apps/http-server/src/index.js +559 -43
- package/dist/apps/http-server/src/public/assets/index-Bl059Jd0.js +9 -0
- package/dist/apps/http-server/src/public/assets/index-CaPxN_Ez.css +1 -0
- package/dist/apps/http-server/src/public/index.html +2 -2
- package/dist/apps/mcp-server/src/index.js +55 -9
- package/dist/packages/core/src/capability-profile.js +145 -0
- package/dist/packages/core/src/index.js +4 -0
- package/dist/packages/core/src/mcp-preflight.js +80 -0
- package/dist/packages/core/src/registry.js +128 -3
- package/dist/packages/core/src/request-store.js +158 -0
- package/dist/packages/core/src/response-parser.js +76 -8
- package/dist/packages/core/src/router.js +408 -13
- package/dist/packages/core/src/runtime-paths.js +16 -0
- package/dist/packages/core/src/version.js +57 -0
- package/dist/packages/mcp/src/provider.js +57 -6
- package/dist/packages/transports/src/headless/headless-transport.js +13 -8
- package/dist/packages/transports/src/tmux/tmux-client.js +334 -0
- package/dist/packages/transports/src/tmux/tmux-transport.js +10 -0
- package/docs/DEVELOPER-START-GUIDE.md +9 -1
- package/docs/GETTING-STARTED.md +10 -5
- package/docs/HTTP-UI-MCP.md +39 -13
- package/docs/MCP-CLIENT-SETUP.md +56 -3
- package/docs/SKILL-INSTALL-GUIDE.md +21 -3
- package/docs/TROUBLESHOOTING.md +51 -2
- package/docs/USER-START-GUIDE.md +157 -26
- package/docs/assets/mina-ai-router-overview.svg +109 -0
- package/package.json +8 -2
- package/dist/apps/http-server/src/public/assets/index-Be0tne90.js +0 -9
- package/dist/apps/http-server/src/public/assets/index-CEhd8YGG.css +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--bg:#fcf9f8;--surface:#fff;--surface-low:#f6f3f2;--surface-container:#f0eded;--surface-high:#eae7e7;--ink:#1c1b1b;--muted:#404846;--muted-2:#717976;--outline:#c0c8c5;--outline-soft:#40484624;--primary:#18443b;--primary-container:#315c52;--primary-soft:#beecdf;--secondary:#006a6a;--success:#16815f;--warning:#a65f14;--danger:#ba1a1a;--terminal:#151716;--terminal-ink:#dff5ea;--radius-sm:.25rem;--radius:.375rem;--radius-lg:.5rem;--shadow-menu:0 1rem 2rem #1e2d2724;--font-ui:"Work Sans", ui-sans-serif, system-ui, sans-serif;--font-head:"Hanken Grotesk", ui-sans-serif, system-ui, sans-serif;--font-mono:"JetBrains Mono", "SFMono-Regular", ui-monospace, Menlo, Consolas, monospace}*{box-sizing:border-box}body{background:radial-gradient(circle at 14% 10%, #315c5214, transparent 26rem), radial-gradient(circle at 92% 18%, #006a6a0f, transparent 24rem), linear-gradient(180deg, #ffffff80, transparent 18rem), var(--bg);min-width:0;min-height:100vh;color:var(--ink);font-family:var(--font-ui);letter-spacing:0;margin:0;font-size:1rem;overflow:hidden}button,input,select,textarea{font:inherit}h1,h2,h3,p{margin:0}h1,h2,h3{font-family:var(--font-head)}code,pre,.mono{font-family:var(--font-mono)}.app-shell{grid-template-rows:auto minmax(0,1fr);height:100vh;min-height:100vh;display:grid}.workspace{grid-template-columns:minmax(0,1fr);min-width:0;min-height:0;display:grid;overflow:hidden}.top-command,.brand-block,.status-cluster,.command-actions,.toolbar,.actions,.meta-row,.agent-title,.request-top,.flow-head,.inspector-head,.activity-head,.modal-head,.terminal-head{align-items:center;gap:.6rem;min-width:0;display:flex}.top-command,.flow-head,.inspector-head,.activity-head,.modal-head{justify-content:space-between}.top-command{border-bottom:1px solid var(--outline-soft);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);background:#fcf9f8eb;flex-wrap:wrap;min-height:3.5rem;padding:.5rem 1rem}.status-cluster,.command-actions,.actions,.toolbar{flex-wrap:wrap}.side-rail{border-right:1px solid var(--outline-soft);background:#ffffff8f;flex-direction:column;align-items:center;gap:.65rem;padding:1rem .7rem;display:flex}.canvas-panel{grid-template-rows:minmax(0,1fr);min-width:0;min-height:0;display:grid}.metrics{grid-template-columns:repeat(4,minmax(0,1fr));gap:.6rem;padding:.75rem 1rem;display:grid}.flow-card{border:1px solid var(--outline-soft);border-radius:var(--radius-lg);background:#ffffffb8;grid-template-rows:auto minmax(0,1fr);min-width:0;min-height:0;margin:1rem;display:grid;overflow:hidden}.flow-head,.inspector-head,.activity-head,.modal-head{border-bottom:1px solid var(--outline-soft);min-height:3.1rem;padding:.65rem 1rem}.map{cursor:grab;touch-action:none;min-height:21rem;padding:1rem;position:relative;overflow:hidden}.map.is-panning{cursor:grabbing}.flow-stage{transform-origin:50%;position:absolute;inset:1rem}.inspector{-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);background:#ffffffb8;grid-template-rows:auto minmax(0,1fr);min-width:0;min-height:0;display:grid}.floating-inspector{z-index:18;border:1px solid var(--outline-soft);border-radius:var(--radius-lg);width:min(24rem,100vw - 2rem);max-height:calc(100vh - 5.5rem);box-shadow:var(--shadow-menu);will-change:transform;transition:transform .18s,box-shadow .18s;position:fixed;top:clamp(4.5rem,8vh,6rem);bottom:1rem;right:1rem;overflow:hidden}.floating-inspector.is-collapsed{transform:translate(calc(100% - 3rem));box-shadow:0 .8rem 1.8rem #312d2b1f}.floating-inspector.is-collapsed .inspector-head,.floating-inspector.is-collapsed .inspector-body{pointer-events:none;opacity:0}.inspector-tab{z-index:2;border-radius:var(--radius-lg) 0 0 var(--radius-lg);border:1px solid var(--outline-soft);background:var(--primary-container);color:#fff;border-right:0;align-content:center;justify-items:center;gap:.35rem;width:3rem;min-height:7rem;padding:.55rem .2rem;display:grid;position:absolute;top:1rem;left:0;box-shadow:-.6rem .8rem 1.6rem #312d2b29}.inspector-tab span:not(.material-symbols-outlined){writing-mode:vertical-rl;text-orientation:mixed;font-family:var(--font-mono);font-size:.72rem;font-weight:700}.inspector-body,.modal-body{align-content:start;gap:.85rem;min-width:0;padding:.85rem 1rem;display:grid;overflow:auto}.activity{border-top:1px solid var(--outline-soft);background:#fffc;grid-template-rows:auto minmax(0,1fr);min-height:0;display:grid}.floating-activity{z-index:12;position:fixed;bottom:0;left:0;right:0;overflow:hidden;box-shadow:0 -.8rem 2rem #312d2b14}.activity-resize-handle{cursor:row-resize;touch-action:none;height:.7rem;position:absolute;top:-.35rem;left:0;right:0}.activity-resize-handle:before{content:"";background:#535c5938;border-radius:999px;width:3.5rem;height:.22rem;position:absolute;top:.22rem;left:50%;transform:translate(-50%)}.activity-body{padding:.35rem .75rem .65rem;overflow:auto}@media (width>=48rem){.detail-grid,.form-grid{grid-template-columns:1fr 1fr}}@media (width>=68.01rem){.top-command{flex-wrap:nowrap}.status-cluster{flex:auto;justify-content:center}.command-actions{flex-wrap:nowrap;flex:none}}@media (width<=68rem){body{overflow:auto}.app-shell{grid-template-rows:auto auto auto;height:auto}.top-command{flex-direction:column;align-items:flex-start}.side-rail{border-right:0;border-bottom:1px solid var(--outline-soft);flex-direction:row;overflow-x:auto}.rail-button:last-child{margin-top:0;margin-left:auto}.metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.floating-inspector{width:min(23rem,100vw - 1.5rem);max-height:min(34rem,100vh - 1.5rem);top:auto;bottom:.75rem;right:.75rem}}@media (width<=47.99rem){.command-actions,.status-cluster{width:100%}.command-actions button{flex:10rem}.metrics{grid-template-columns:1fr}.flow-card{margin:.75rem}.map{cursor:default;touch-action:auto;gap:.75rem;min-height:auto;display:grid}.flow-stage{gap:.75rem;display:grid;position:static;inset:auto;transform:none!important}.links{display:none}.router-node,.agent-node{touch-action:auto;width:100%;position:static;transform:none}.router-node{order:-1}.floating-inspector{border-radius:var(--radius-lg) var(--radius-lg) 0 0;width:100vw;max-height:72vh;bottom:0;right:0}}button{border-radius:var(--radius);background:var(--primary-container);color:#fff;cursor:pointer;border:1px solid #0000;justify-content:center;align-items:center;gap:.45rem;min-height:2.75rem;padding:.62rem .85rem;display:inline-flex}.material-symbols-outlined{letter-spacing:normal;text-transform:none;white-space:nowrap;word-wrap:normal;-webkit-font-feature-settings:"liga";-webkit-font-smoothing:antialiased;direction:ltr;font-style:normal;font-weight:400;line-height:1;display:inline-block;font-family:Material Symbols Outlined!important}button.secondary{background:var(--surface);color:var(--ink);border-color:var(--outline-soft)}button.ghost{color:var(--muted);background:0 0}button.danger{color:var(--danger);background:#fff5f3;border-color:#ba1a1a40}button:disabled{cursor:wait;opacity:.55}button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline-offset:2px;outline:2px solid #006a6a52}input,select,textarea{border:1px solid var(--outline-soft);border-radius:var(--radius);background:var(--surface-low);width:100%;min-height:2.75rem;color:var(--ink);padding:.72rem}textarea{resize:vertical;min-height:8rem}label{color:var(--muted);gap:.35rem;font-size:.875rem;display:grid}code,pre{border-radius:var(--radius-sm);background:var(--surface-container)}code{overflow-wrap:anywhere;padding:.12rem .28rem}pre{white-space:pre-wrap;overflow-wrap:anywhere;max-height:22rem;margin:0;padding:.75rem;overflow:auto}.brand-mark{border-radius:var(--radius-lg);background:var(--primary-container);color:#fff;place-items:center;width:2rem;height:2rem;display:grid}.brand-title{gap:.08rem;display:grid}.brand-title h1{font-size:1.08rem;line-height:1.15}.subtitle{color:var(--muted);font-size:.82rem;line-height:1.35}.chip{border:1px solid var(--outline-soft);border-radius:var(--radius);max-width:100%;min-height:1.85rem;color:var(--muted);font-family:var(--font-mono);white-space:nowrap;text-overflow:ellipsis;overflow-wrap:anywhere;background:#ffffffb8;flex:auto;align-items:center;gap:.4rem;padding:.35rem .55rem;font-size:.75rem;display:inline-flex;overflow:hidden}.status-cluster .chip{max-width:22rem}.status-cluster .chip:first-child,.flow-head .chip{flex:none}.chip-copy{max-width:min(34rem,100%)}.dot{background:var(--muted-2);border-radius:999px;flex:none;width:.55rem;height:.55rem}.dot.available,.dot.answered,.dot.success{background:var(--success)}.dot.waiting,.dot.busy,.dot.sent,.dot.created,.dot.stale{background:var(--warning)}.dot.failed,.dot.timeout,.dot.missing,.dot.needs-attention,.dot.danger{background:var(--danger)}.rail-button{width:2.75rem;height:2.75rem;color:var(--muted);font-family:var(--font-mono);background:0 0;border-color:#0000;place-items:center;padding:0;display:grid}.rail-button.active{color:var(--secondary);background:#006a6a1a;border-color:#006a6a2e}.rail-button:last-child{margin-top:auto}.metric{border:1px solid var(--outline-soft);border-radius:var(--radius-lg);background:#ffffffad;padding:.65rem .75rem}.metric span{color:var(--muted);font-family:var(--font-mono);align-items:center;gap:.3rem;font-size:.72rem;display:inline-flex}.metric strong{font-family:var(--font-head);margin-top:.3rem;font-size:1.25rem;line-height:1;display:block}.links{pointer-events:none;width:100%;height:100%;position:absolute;inset:0}.link-line{stroke:#4048464d;stroke-width:2px;stroke-dasharray:7 8}.link-line.answered{stroke:#16815fb8;stroke-dasharray:0}.link-line.waiting,.link-line.sent,.link-line.created{stroke:#a65f14d1;stroke-width:3px;animation:1.5s ease-in-out infinite flowPulse}.link-line.failed,.link-line.timeout{stroke:#ba1a1ac7;stroke-dasharray:4 6}.link-line.selected{stroke-width:4px}.router-node,.agent-node{border:1px solid var(--outline-soft);border-radius:var(--radius-lg);background:var(--surface);position:absolute}.router-node{z-index:3;border:2px solid #315c52c7;align-content:center;justify-items:center;gap:.55rem;width:min(14rem,64vw);min-height:9rem;padding:1rem;display:grid;top:50%;left:50%;transform:translate(-50%,-50%)}.router-icon{width:2.6rem;height:2.6rem;color:var(--primary-container);font-family:var(--font-mono);background:#315c521a;border-radius:999px;place-items:center;font-weight:800;display:grid}.legend{color:var(--muted);font-family:var(--font-mono);flex-wrap:wrap;justify-content:center;align-items:center;gap:.45rem;font-size:.72rem;display:flex}.agent-node{width:min(14rem,42vw);min-height:7.7rem;color:var(--ink);cursor:pointer;touch-action:none;-webkit-user-select:none;user-select:none;text-align:left;z-index:4;gap:.45rem;padding:.78rem;display:grid;overflow:hidden}.agent-node:hover,.agent-node.selected{border-color:#006a6a8c;box-shadow:0 .9rem 1.8rem #006a6a1a}.agent-node:active{cursor:grabbing}.agent-node.active-request{border-color:#a65f149e}.agent-node.failed-request{border-color:#ba1a1a80}.agent-title,.request-top{justify-content:space-between}.agent-id{overflow-wrap:anywhere;min-width:0;font-family:var(--font-mono);align-items:center;gap:.35rem;font-size:.82rem;font-weight:700;display:inline-flex}.node-meta,.muted{color:var(--muted)}.node-meta{overflow-wrap:anywhere;font-size:.75rem;line-height:1.35}.request-summary{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.status{border:1px solid var(--outline-soft);border-radius:var(--radius);min-height:1.5rem;color:var(--muted);font-family:var(--font-mono);white-space:nowrap;background:#fff9;align-items:center;gap:.35rem;padding:.12rem .48rem;font-size:.7rem;display:inline-flex}.status.available,.status.answered{color:var(--success);background:#16815f14;border-color:#16815f4d}.status.waiting,.status.sent,.status.created,.status.busy{color:var(--warning);background:#a65f1414;border-color:#a65f1452}.status.missing,.status.needs-attention,.status.timeout,.status.failed,.status.parse_failure,.status.transport_failure,.status.unknown_failure{color:var(--danger);background:#ba1a1a14;border-color:#ba1a1a47}.status.cancelled,.status.archived,.status.unknown{color:var(--muted-2);background:#71797614}.status.fresh{color:var(--success);background:#16815f14;border-color:#16815f4d}.status.manual{color:var(--secondary);background:#006a6a14;border-color:#006a6a47}.status.stale{color:var(--warning);background:#a65f1414;border-color:#a65f1452}.status.missing{color:var(--danger);background:#ba1a1a14;border-color:#ba1a1a47}.section,.request-list,.detail-grid,.health-grid,.form-grid,.guide-steps{gap:.75rem;display:grid}.health-grid{grid-template-columns:repeat(auto-fit,minmax(8.5rem,1fr))}.section-title{color:var(--muted);font-family:var(--font-mono);letter-spacing:.04em;text-transform:uppercase;align-items:center;gap:.35rem;font-size:.68rem;font-weight:700;display:inline-flex}.kv{gap:.3rem;min-width:0;display:grid}.kv span{color:var(--muted);font-size:.78rem}.kv strong,.kv code{overflow-wrap:anywhere;min-width:0}.capability-card{border:1px solid var(--outline-soft);border-radius:var(--radius-lg);background:#ffffff85;gap:.55rem;min-width:0;padding:.72rem;display:grid}.capability-card p{overflow-wrap:anywhere;min-width:0;line-height:1.45}.capability-card-head,.capability-meta,.capability-actions{flex-wrap:wrap;align-items:center;gap:.45rem;min-width:0;display:flex}.capability-card-head{justify-content:space-between}.capability-meta{color:var(--muted);font-family:var(--font-mono);font-size:.72rem;line-height:1.4}.capability-meta span{overflow-wrap:anywhere;min-width:0}.capability-actions button{flex:10rem}.capability-status{text-transform:uppercase}.status.quality-strong,.dot.quality-strong{color:var(--success);background:#16815f1f;border-color:#16815f3d}.status.quality-thin,.dot.quality-thin{color:var(--warning);background:#a65f141f;border-color:#a65f143d}.status.quality-missing,.dot.quality-missing{color:var(--danger);background:#ba1a1a1a;border-color:#ba1a1a38}.capability-profile{border-top:1px solid var(--outline-soft);gap:.55rem;min-width:0;padding-top:.55rem;display:grid}.profile-row{gap:.28rem;min-width:0;display:grid}.profile-row>span:not(.subtitle){overflow-wrap:anywhere;min-width:0;line-height:1.4}.profile-tags{flex-wrap:wrap;gap:.35rem;min-width:0;display:flex}.profile-tag{border:1px solid var(--outline-soft);border-radius:var(--radius-sm);min-width:0;max-width:100%;color:var(--ink);font-family:var(--font-mono);overflow-wrap:anywhere;background:#ffffffad;padding:.25rem .42rem;font-size:.72rem;line-height:1.35}.capability-fresh{background:#16815f0d;border-color:#16815f42}.capability-manual{background:#006a6a0d;border-color:#006a6a3d}.capability-stale{background:#a65f140f;border-color:#a65f1447}.capability-missing{background:#ba1a1a0d;border-color:#ba1a1a3d}.request-detail{min-width:0}.request-route{flex-wrap:wrap;align-items:center;gap:.35rem;min-width:0;line-height:1.45;display:flex}.request-route code{overflow-wrap:anywhere}.diagnostic-section{border:1px solid var(--outline-soft);border-radius:var(--radius);background:#ffffff7a;gap:.55rem;min-width:0;padding:.7rem;display:grid}.diagnostic-grid{grid-template-columns:1fr;gap:.6rem;min-width:0;display:grid}.diagnostic-section pre{white-space:pre-wrap;overflow-wrap:anywhere;border:1px solid var(--outline-soft);border-radius:var(--radius);background:var(--terminal);min-width:0;max-height:15rem;color:var(--terminal-ink);font-family:var(--font-mono);margin:0;padding:.65rem;font-size:.78rem;line-height:1.45;overflow:auto}.recovery-banner{border-radius:var(--radius);color:var(--muted);background:#a65f1412;border:1px solid #a65f1447;padding:.65rem;line-height:1.45}.recovery-events{gap:.5rem;display:grid}.recovery-event{border:1px solid var(--outline-soft);border-radius:var(--radius);background:#ffffff85;gap:.3rem;min-width:0;padding:.6rem;display:grid}.recovery-event p,.recovery-event code{overflow-wrap:anywhere}@media (width>=768px){.diagnostic-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}.command-box,.notice{border:1px solid var(--outline-soft);border-radius:var(--radius-lg);padding:.65rem}.command-box{background:var(--terminal);color:var(--terminal-ink);gap:.55rem;display:grid}.command-box code{color:#9af1f1;background:#0000003d;padding:.6rem;display:block}.notice{background:var(--surface-low);color:var(--muted);font-size:.86rem;line-height:1.45}.notice-recovery{gap:.55rem;display:grid}.activity-table{border-collapse:collapse;width:100%;min-width:48rem;font-family:var(--font-mono);font-size:.75rem}.activity-table th,.activity-table td{border-bottom:1px solid var(--outline-soft);text-align:left;vertical-align:top;padding:.42rem .6rem}.activity-table th{color:var(--muted);text-transform:uppercase;font-size:.68rem}.request-item{border:1px solid var(--outline-soft);border-radius:var(--radius-lg);background:var(--surface);width:100%;min-height:4.8rem;color:var(--ink);text-align:left;gap:.42rem;padding:.75rem;display:grid}.empty{text-align:center;min-height:10rem;color:var(--muted);border:1px dashed var(--outline);border-radius:var(--radius-lg);place-items:center;padding:1rem;display:grid}.context-menu{z-index:20;border:1px solid var(--outline-soft);border-radius:var(--radius-lg);width:min(18rem,100vw - 2rem);box-shadow:var(--shadow-menu);background:#fffffffa;padding:.45rem;position:fixed}.context-menu button{width:100%;color:var(--ink);text-align:left;background:0 0;border-color:#0000;justify-content:flex-start}.context-menu .danger{color:var(--danger)}.modal-backdrop{z-index:30;background:#1c1b1b57;place-items:center;padding:1rem;display:grid;position:fixed;inset:0}.modal{border:1px solid var(--outline-soft);border-radius:var(--radius-lg);background:var(--surface);grid-template-rows:auto minmax(0,1fr);width:min(48rem,100%);max-height:min(46rem,100vh - 2rem);display:grid;overflow:hidden;box-shadow:0 1.5rem 4rem #1c1b1b3d}.directory-picker,.terminal-panel{border:1px solid var(--outline-soft);border-radius:var(--radius-lg);grid-column:1/-1;gap:.6rem;padding:.75rem;display:grid}.directory-picker{background:var(--surface)}.directory-list{gap:.35rem;max-height:15rem;display:grid;overflow:auto}.directory-row{background:var(--surface-low);width:100%;min-height:2.5rem;color:var(--ink);border-color:var(--outline-soft);text-align:left;justify-content:space-between;align-items:center;gap:.75rem;display:flex}.terminal-panel{background:var(--terminal);color:var(--terminal-ink)}.terminal-actions{flex-wrap:wrap;gap:.5rem;display:flex}.terminal-panel pre{min-height:16rem;max-height:24rem;color:var(--terminal-ink);background:#080d0a;border:1px solid #d8f5df29}.terminal-panel input{color:var(--terminal-ink);background:#080d0a;border-color:#d8f5df3d}.hidden{display:none!important}@keyframes flowPulse{0%,to{opacity:.42}50%{opacity:1}}
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
8
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
9
|
<link href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@600;700&family=JetBrains+Mono:wght@400;500;700&family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,400,0,0&family=Work+Sans:wght@400;500;600&display=swap" rel="stylesheet" />
|
|
10
|
-
<script type="module" crossorigin src="/assets/index-
|
|
11
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
10
|
+
<script type="module" crossorigin src="/assets/index-Bl059Jd0.js"></script>
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CaPxN_Ez.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
14
14
|
<div id="root"></div>
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
const node_path_1 = require("node:path");
|
|
5
4
|
const node_url_1 = require("node:url");
|
|
6
5
|
const node_fs_1 = require("node:fs");
|
|
7
6
|
const src_1 = require("../../../packages/core/src");
|
|
8
7
|
const src_2 = require("../../../packages/transports/src");
|
|
9
8
|
const importEsm = new Function("specifier", "return import(specifier)");
|
|
10
|
-
const runtimeModuleUrl = (0, node_url_1.pathToFileURL)(
|
|
11
|
-
const statePath = process.env.MINA_ROUTER_STATE ?? (0,
|
|
9
|
+
const runtimeModuleUrl = (0, node_url_1.pathToFileURL)(require.resolve("@minasoft/mcp-runtime")).href;
|
|
10
|
+
const statePath = process.env.MINA_ROUTER_STATE ?? (0, src_1.defaultRouterStatePath)();
|
|
12
11
|
const logPath = process.env.MINA_MCP_LOG;
|
|
13
12
|
const context = createContext();
|
|
14
13
|
let inputBuffer = Buffer.alloc(0);
|
|
@@ -102,7 +101,11 @@ async function createProvider() {
|
|
|
102
101
|
description: "List registered Mina helper agents.",
|
|
103
102
|
inputSchema: {
|
|
104
103
|
type: "object",
|
|
105
|
-
properties: {
|
|
104
|
+
properties: {
|
|
105
|
+
callerAgentId: { type: "string" },
|
|
106
|
+
sourceAgent: { type: "string" },
|
|
107
|
+
callerSessionFingerprint: { type: "string" },
|
|
108
|
+
},
|
|
106
109
|
additionalProperties: false,
|
|
107
110
|
},
|
|
108
111
|
},
|
|
@@ -117,11 +120,15 @@ async function createProvider() {
|
|
|
117
120
|
agentType: { type: "string" },
|
|
118
121
|
transport: { type: "string" },
|
|
119
122
|
sessionId: { type: "string" },
|
|
123
|
+
sessionFingerprint: { type: "string" },
|
|
120
124
|
projectRoot: { type: "string" },
|
|
121
125
|
tmuxTarget: { type: "string" },
|
|
122
126
|
startupCommand: { type: "string" },
|
|
123
127
|
capabilitySummary: { type: "string" },
|
|
124
128
|
capabilitySources: { type: "string" },
|
|
129
|
+
capabilitySource: { type: "string" },
|
|
130
|
+
capabilityUpdatedAt: { type: "string" },
|
|
131
|
+
lastCapabilityRefreshAt: { type: "string" },
|
|
125
132
|
},
|
|
126
133
|
required: ["id", "agentType", "transport", "sessionId", "projectRoot"],
|
|
127
134
|
additionalProperties: false,
|
|
@@ -135,6 +142,10 @@ async function createProvider() {
|
|
|
135
142
|
properties: {
|
|
136
143
|
target: { type: "string" },
|
|
137
144
|
task: { type: "string" },
|
|
145
|
+
sourceAgent: { type: "string" },
|
|
146
|
+
callerAgentId: { type: "string" },
|
|
147
|
+
callerSessionFingerprint: { type: "string" },
|
|
148
|
+
allowSelfCall: { type: "boolean" },
|
|
138
149
|
timeoutMs: { type: "number" },
|
|
139
150
|
},
|
|
140
151
|
required: ["target", "task"],
|
|
@@ -157,7 +168,7 @@ async function createProvider() {
|
|
|
157
168
|
providerPromise = Promise.resolve({
|
|
158
169
|
serverInfo: {
|
|
159
170
|
name: "mina-ai-router",
|
|
160
|
-
version:
|
|
171
|
+
version: (0, src_1.packageVersion)(),
|
|
161
172
|
},
|
|
162
173
|
tools: {
|
|
163
174
|
async list() {
|
|
@@ -173,16 +184,28 @@ async function createProvider() {
|
|
|
173
184
|
async function callTool(name, args) {
|
|
174
185
|
switch (name) {
|
|
175
186
|
case "list_agents": {
|
|
176
|
-
const
|
|
187
|
+
const caller = resolveCallerAgent(args);
|
|
188
|
+
const agents = await context.router.listAgentStatuses({ callerAgentId: caller?.id });
|
|
177
189
|
return jsonToolResult({ agents });
|
|
178
190
|
}
|
|
179
191
|
case "register_agent": {
|
|
180
192
|
try {
|
|
181
193
|
const agent = agentFromArgs(args);
|
|
182
|
-
|
|
194
|
+
const now = new Date().toISOString();
|
|
195
|
+
const registered = context.registry.register({
|
|
196
|
+
...agent,
|
|
197
|
+
bootstrapStatus: "ready",
|
|
198
|
+
registrationSource: "mcp",
|
|
199
|
+
registrationStatus: "confirmed",
|
|
200
|
+
lastRegistrationAttemptAt: now,
|
|
201
|
+
confirmedByAgentAt: now,
|
|
202
|
+
sessionFingerprint: agent.sessionFingerprint ?? agent.sessionId,
|
|
203
|
+
}, {
|
|
204
|
+
capabilitySource: agent.capabilitySummary || agent.capabilitySources ? "generated" : undefined,
|
|
205
|
+
});
|
|
183
206
|
context.save();
|
|
184
207
|
const agents = await context.router.listAgentStatuses();
|
|
185
|
-
return jsonToolResult({ agent, agents });
|
|
208
|
+
return jsonToolResult({ agent: registered, agents });
|
|
186
209
|
}
|
|
187
210
|
catch (error) {
|
|
188
211
|
return {
|
|
@@ -195,11 +218,19 @@ async function callTool(name, args) {
|
|
|
195
218
|
const target = typeof args.target === "string" ? args.target : "";
|
|
196
219
|
const task = typeof args.task === "string" ? args.task : "";
|
|
197
220
|
const timeoutMs = typeof args.timeoutMs === "number" ? args.timeoutMs : undefined;
|
|
221
|
+
const caller = resolveCallerAgent(args);
|
|
222
|
+
const allowSelfCall = args.allowSelfCall === true;
|
|
198
223
|
if (!target || !task) {
|
|
199
224
|
return { kind: "invalid_params", message: "call_agent requires string target and task." };
|
|
200
225
|
}
|
|
201
226
|
try {
|
|
202
|
-
const response = await context.router.callAgent({
|
|
227
|
+
const response = await context.router.callAgent({
|
|
228
|
+
sourceAgent: caller?.id,
|
|
229
|
+
target,
|
|
230
|
+
task,
|
|
231
|
+
timeoutMs,
|
|
232
|
+
allowSelfCall,
|
|
233
|
+
});
|
|
203
234
|
return jsonToolResult(response);
|
|
204
235
|
}
|
|
205
236
|
catch (error) {
|
|
@@ -235,6 +266,14 @@ async function callTool(name, args) {
|
|
|
235
266
|
return { kind: "not_found", message: `Unknown tool "${name}".` };
|
|
236
267
|
}
|
|
237
268
|
}
|
|
269
|
+
function resolveCallerAgent(args) {
|
|
270
|
+
const callerAgentId = stringValue(args.callerAgentId) ?? stringValue(args.sourceAgent);
|
|
271
|
+
if (callerAgentId) {
|
|
272
|
+
return context.registry.get(callerAgentId);
|
|
273
|
+
}
|
|
274
|
+
const fingerprint = stringValue(args.callerSessionFingerprint);
|
|
275
|
+
return fingerprint ? context.registry.findBySessionFingerprint(fingerprint) : undefined;
|
|
276
|
+
}
|
|
238
277
|
function agentFromArgs(args) {
|
|
239
278
|
const id = requiredString(args.id, "id");
|
|
240
279
|
return {
|
|
@@ -243,11 +282,15 @@ function agentFromArgs(args) {
|
|
|
243
282
|
agentType: requiredString(args.agentType, "agentType"),
|
|
244
283
|
transport: requiredString(args.transport, "transport"),
|
|
245
284
|
sessionId: requiredString(args.sessionId, "sessionId"),
|
|
285
|
+
sessionFingerprint: stringValue(args.sessionFingerprint) ?? stringValue(args.sessionId),
|
|
246
286
|
projectRoot: requiredString(args.projectRoot, "projectRoot"),
|
|
247
287
|
tmuxTarget: stringValue(args.tmuxTarget),
|
|
248
288
|
startupCommand: stringValue(args.startupCommand),
|
|
249
289
|
capabilitySummary: stringValue(args.capabilitySummary),
|
|
250
290
|
capabilitySources: stringValue(args.capabilitySources),
|
|
291
|
+
capabilitySource: capabilitySourceValue(args.capabilitySource),
|
|
292
|
+
capabilityUpdatedAt: stringValue(args.capabilityUpdatedAt),
|
|
293
|
+
lastCapabilityRefreshAt: stringValue(args.lastCapabilityRefreshAt),
|
|
251
294
|
};
|
|
252
295
|
}
|
|
253
296
|
function requiredString(value, field) {
|
|
@@ -259,6 +302,9 @@ function requiredString(value, field) {
|
|
|
259
302
|
function stringValue(value) {
|
|
260
303
|
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
261
304
|
}
|
|
305
|
+
function capabilitySourceValue(value) {
|
|
306
|
+
return value === "manual" || value === "generated" ? value : undefined;
|
|
307
|
+
}
|
|
262
308
|
function jsonToolResult(value) {
|
|
263
309
|
const text = JSON.stringify(value, null, 2);
|
|
264
310
|
return {
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildCapabilityProfile = buildCapabilityProfile;
|
|
4
|
+
exports.scoreCapabilityProfile = scoreCapabilityProfile;
|
|
5
|
+
function buildCapabilityProfile(input) {
|
|
6
|
+
const summary = input.capabilitySummary?.trim();
|
|
7
|
+
const profile = normalizeProfile(input.capabilityProfile);
|
|
8
|
+
const quality = scoreCapabilityProfile({ ...input, capabilityProfile: profile });
|
|
9
|
+
if (!summary && !profile) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
...profile,
|
|
14
|
+
quality: quality.quality,
|
|
15
|
+
qualityReasons: quality.reasons,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function scoreCapabilityProfile(input) {
|
|
19
|
+
const summary = input.capabilitySummary?.trim() ?? "";
|
|
20
|
+
const sources = splitList(input.capabilitySources);
|
|
21
|
+
const profile = normalizeProfile(input.capabilityProfile);
|
|
22
|
+
const canAnswer = profile?.canAnswer ?? [];
|
|
23
|
+
const evidence = profile?.evidence?.length ? profile.evidence : sources;
|
|
24
|
+
const keyAreas = profile?.keyAreas ?? [];
|
|
25
|
+
const languageCount = profile?.primaryLanguages?.length ?? 0;
|
|
26
|
+
const purpose = profile?.projectPurpose?.trim() ?? "";
|
|
27
|
+
const text = [summary, purpose, canAnswer.join(" "), keyAreas.join(" ")].join(" ").toLowerCase();
|
|
28
|
+
if (!summary && !purpose && canAnswer.length === 0 && keyAreas.length === 0 && evidence.length === 0) {
|
|
29
|
+
return { quality: "missing", reasons: ["No capability summary or structured profile is recorded."] };
|
|
30
|
+
}
|
|
31
|
+
const genericHits = genericCapabilitySignals.filter((signal) => text.includes(signal));
|
|
32
|
+
const domainHits = domainCapabilitySignals.filter((signal) => text.includes(signal));
|
|
33
|
+
const hasStructuredStrongShape = Boolean(purpose)
|
|
34
|
+
&& canAnswer.length >= 2
|
|
35
|
+
&& keyAreas.length >= 1
|
|
36
|
+
&& evidence.length >= 2;
|
|
37
|
+
const hasSummaryStrongShape = domainHits.length >= 3
|
|
38
|
+
&& evidence.length >= 2
|
|
39
|
+
&& !mostlyGenericFileMention(summary);
|
|
40
|
+
if (hasStructuredStrongShape || hasSummaryStrongShape) {
|
|
41
|
+
return {
|
|
42
|
+
quality: "strong",
|
|
43
|
+
reasons: [
|
|
44
|
+
hasStructuredStrongShape
|
|
45
|
+
? "Structured profile includes purpose, answerable areas, key areas, and evidence."
|
|
46
|
+
: "Capability summary includes multiple answerable domains backed by evidence.",
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (genericHits.length > 0 || mostlyGenericFileMention(summary) || domainHits.length < 2 || evidence.length === 0) {
|
|
51
|
+
return {
|
|
52
|
+
quality: "thin",
|
|
53
|
+
reasons: [
|
|
54
|
+
genericHits.length > 0
|
|
55
|
+
? `Summary leans on generic file references: ${genericHits.slice(0, 3).join(", ")}.`
|
|
56
|
+
: "Profile does not yet describe enough answerable domains with evidence.",
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (languageCount > 0 && (canAnswer.length > 0 || keyAreas.length > 0)) {
|
|
61
|
+
return {
|
|
62
|
+
quality: "thin",
|
|
63
|
+
reasons: ["Profile has some structure but needs more answerable domains and evidence."],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return { quality: "thin", reasons: ["Capability profile is present but not yet strong."] };
|
|
67
|
+
}
|
|
68
|
+
function normalizeProfile(profile) {
|
|
69
|
+
if (!profile) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
const normalized = {
|
|
73
|
+
projectPurpose: stringValue(profile.projectPurpose),
|
|
74
|
+
primaryLanguages: cleanList(profile.primaryLanguages),
|
|
75
|
+
keyAreas: cleanList(profile.keyAreas),
|
|
76
|
+
canAnswer: cleanList(profile.canAnswer),
|
|
77
|
+
cannotAnswerYet: cleanList(profile.cannotAnswerYet),
|
|
78
|
+
evidence: cleanList(profile.evidence),
|
|
79
|
+
};
|
|
80
|
+
if (!normalized.projectPurpose
|
|
81
|
+
&& normalized.primaryLanguages.length === 0
|
|
82
|
+
&& normalized.keyAreas.length === 0
|
|
83
|
+
&& normalized.canAnswer.length === 0
|
|
84
|
+
&& normalized.cannotAnswerYet.length === 0
|
|
85
|
+
&& normalized.evidence.length === 0) {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
return normalized;
|
|
89
|
+
}
|
|
90
|
+
function cleanList(value) {
|
|
91
|
+
return Array.isArray(value)
|
|
92
|
+
? value.map((item) => item.trim()).filter(Boolean)
|
|
93
|
+
: [];
|
|
94
|
+
}
|
|
95
|
+
function splitList(value) {
|
|
96
|
+
return value
|
|
97
|
+
? value.split(/[,\n]/).map((item) => item.trim()).filter(Boolean)
|
|
98
|
+
: [];
|
|
99
|
+
}
|
|
100
|
+
function stringValue(value) {
|
|
101
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
102
|
+
}
|
|
103
|
+
function mostlyGenericFileMention(summary) {
|
|
104
|
+
if (!summary) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
const lower = summary.toLowerCase();
|
|
108
|
+
const fileMentionCount = (lower.match(/\b(readme|claude\.md|agents\.md|src|package\.json|docs?|directories|files?)\b/g) ?? []).length;
|
|
109
|
+
return fileMentionCount >= 2 && (lower.includes("provides guidance") || lower.includes("contains") || lower.includes("there is"));
|
|
110
|
+
}
|
|
111
|
+
const genericCapabilitySignals = [
|
|
112
|
+
"claude.md",
|
|
113
|
+
"agents.md",
|
|
114
|
+
"readme",
|
|
115
|
+
"src directory",
|
|
116
|
+
"provides guidance",
|
|
117
|
+
"project files",
|
|
118
|
+
"files exist",
|
|
119
|
+
"directory",
|
|
120
|
+
"package.json",
|
|
121
|
+
];
|
|
122
|
+
const domainCapabilitySignals = [
|
|
123
|
+
"api",
|
|
124
|
+
"endpoint",
|
|
125
|
+
"router",
|
|
126
|
+
"mcp",
|
|
127
|
+
"transport",
|
|
128
|
+
"tmux",
|
|
129
|
+
"request",
|
|
130
|
+
"response",
|
|
131
|
+
"schema",
|
|
132
|
+
"database",
|
|
133
|
+
"auth",
|
|
134
|
+
"payment",
|
|
135
|
+
"frontend",
|
|
136
|
+
"backend",
|
|
137
|
+
"cli",
|
|
138
|
+
"web ui",
|
|
139
|
+
"http",
|
|
140
|
+
"react",
|
|
141
|
+
"typescript",
|
|
142
|
+
"node",
|
|
143
|
+
"test",
|
|
144
|
+
"workflow",
|
|
145
|
+
];
|
|
@@ -15,10 +15,14 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./ids"), exports);
|
|
18
|
+
__exportStar(require("./capability-profile"), exports);
|
|
18
19
|
__exportStar(require("./file-state"), exports);
|
|
20
|
+
__exportStar(require("./mcp-preflight"), exports);
|
|
19
21
|
__exportStar(require("./prompt-envelope"), exports);
|
|
20
22
|
__exportStar(require("./registry"), exports);
|
|
21
23
|
__exportStar(require("./request-store"), exports);
|
|
22
24
|
__exportStar(require("./response-parser"), exports);
|
|
23
25
|
__exportStar(require("./router"), exports);
|
|
26
|
+
__exportStar(require("./runtime-paths"), exports);
|
|
24
27
|
__exportStar(require("./types"), exports);
|
|
28
|
+
__exportStar(require("./version"), exports);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildMcpPreflight = buildMcpPreflight;
|
|
4
|
+
function buildMcpPreflight(input) {
|
|
5
|
+
const mcpName = input.mcpName?.trim() || "mina-ai-router";
|
|
6
|
+
const mcpUrl = input.mcpUrl;
|
|
7
|
+
const commands = commandsFor(input.agentType, mcpName, mcpUrl);
|
|
8
|
+
if (!commands) {
|
|
9
|
+
return result({
|
|
10
|
+
status: "unsupported",
|
|
11
|
+
mcpUrl,
|
|
12
|
+
detail: `MCP preflight does not yet support agent type "${input.agentType}".`,
|
|
13
|
+
nextAction: "Configure this client manually before asking it to self-register with Mina AI Router.",
|
|
14
|
+
canSendSelfRegistrationPrompt: false,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
if (input.configured === true || input.configuredUrl === mcpUrl) {
|
|
18
|
+
return result({
|
|
19
|
+
status: "configured",
|
|
20
|
+
mcpUrl,
|
|
21
|
+
detail: `${input.agentType} MCP appears configured for ${mcpUrl}.`,
|
|
22
|
+
nextAction: "MCP preflight is configured; Mina may send the self-registration prompt.",
|
|
23
|
+
canSendSelfRegistrationPrompt: true,
|
|
24
|
+
...commands,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
if (input.configuredUrl && input.configuredUrl !== mcpUrl) {
|
|
28
|
+
return result({
|
|
29
|
+
status: "stale",
|
|
30
|
+
mcpUrl,
|
|
31
|
+
detail: `${input.agentType} MCP points at ${input.configuredUrl}, but Mina is serving ${mcpUrl}.`,
|
|
32
|
+
nextAction: `Run: ${commands.setupCommand}`,
|
|
33
|
+
canSendSelfRegistrationPrompt: false,
|
|
34
|
+
...commands,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return result({
|
|
38
|
+
status: "missing",
|
|
39
|
+
mcpUrl,
|
|
40
|
+
detail: `${input.agentType} MCP setup is not confirmed for ${mcpUrl}.`,
|
|
41
|
+
nextAction: `Run: ${commands.setupCommand}`,
|
|
42
|
+
canSendSelfRegistrationPrompt: false,
|
|
43
|
+
...commands,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function commandsFor(agentType, mcpName, mcpUrl) {
|
|
47
|
+
if (agentType === "codex") {
|
|
48
|
+
return {
|
|
49
|
+
setupCommand: `codex mcp add ${shellQuote(mcpName)} --url ${shellQuote(mcpUrl)}`,
|
|
50
|
+
verifyCommand: `codex mcp get ${shellQuote(mcpName)}`,
|
|
51
|
+
removeCommand: `codex mcp remove ${shellQuote(mcpName)}`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (agentType === "claude") {
|
|
55
|
+
return {
|
|
56
|
+
setupCommand: `claude mcp add --transport http ${shellQuote(mcpName)} ${shellQuote(mcpUrl)}`,
|
|
57
|
+
verifyCommand: `claude mcp get ${shellQuote(mcpName)}`,
|
|
58
|
+
removeCommand: `claude mcp remove ${shellQuote(mcpName)}`,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
function result(input) {
|
|
64
|
+
return {
|
|
65
|
+
status: input.status,
|
|
66
|
+
mcpPreflightStatus: input.status,
|
|
67
|
+
mcpPreflightDetail: input.detail,
|
|
68
|
+
mcpSetupCommand: input.setupCommand,
|
|
69
|
+
mcpVerifyCommand: input.verifyCommand,
|
|
70
|
+
mcpRemoveCommand: input.removeCommand,
|
|
71
|
+
mcpUrl: input.mcpUrl,
|
|
72
|
+
nextAction: input.nextAction,
|
|
73
|
+
canSendSelfRegistrationPrompt: input.canSendSelfRegistrationPrompt,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function shellQuote(value) {
|
|
77
|
+
if (/^[A-Za-z0-9_./:=@-]+$/.test(value))
|
|
78
|
+
return value;
|
|
79
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
80
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AgentRegistry = void 0;
|
|
4
|
+
const capability_profile_1 = require("./capability-profile");
|
|
4
5
|
class AgentRegistry {
|
|
5
6
|
constructor(agents = []) {
|
|
6
7
|
this.agents = new Map();
|
|
@@ -8,9 +9,67 @@ class AgentRegistry {
|
|
|
8
9
|
this.register(agent);
|
|
9
10
|
}
|
|
10
11
|
}
|
|
11
|
-
register(agent) {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
register(agent, options = {}) {
|
|
13
|
+
const fingerprint = agent.sessionFingerprint;
|
|
14
|
+
const currentById = this.agents.get(agent.id);
|
|
15
|
+
const currentByFingerprint = fingerprint ? this.findBySessionFingerprint(fingerprint) : undefined;
|
|
16
|
+
const current = currentById ?? currentByFingerprint;
|
|
17
|
+
const canonicalId = current?.id ?? agent.id;
|
|
18
|
+
const registrationWarnings = mergeRegistrationWarnings(current?.registrationWarnings, currentByFingerprint && currentByFingerprint.id !== agent.id
|
|
19
|
+
? `Registration id "${agent.id}" matched existing session fingerprint for "${currentByFingerprint.id}"; preserved existing agent id.`
|
|
20
|
+
: undefined);
|
|
21
|
+
const next = {
|
|
22
|
+
...agent,
|
|
23
|
+
id: canonicalId,
|
|
24
|
+
name: currentByFingerprint && currentByFingerprint.id !== agent.id
|
|
25
|
+
? currentByFingerprint.name || currentByFingerprint.id
|
|
26
|
+
: agent.name || current?.name || canonicalId,
|
|
27
|
+
capabilitySummary: agent.capabilitySummary ?? current?.capabilitySummary,
|
|
28
|
+
capabilitySources: agent.capabilitySources ?? current?.capabilitySources,
|
|
29
|
+
capabilitySource: agent.capabilitySource ?? current?.capabilitySource,
|
|
30
|
+
capabilityUpdatedAt: agent.capabilityUpdatedAt ?? current?.capabilityUpdatedAt,
|
|
31
|
+
lastCapabilityRefreshAt: agent.lastCapabilityRefreshAt ?? current?.lastCapabilityRefreshAt,
|
|
32
|
+
capabilityProfile: (0, capability_profile_1.buildCapabilityProfile)({
|
|
33
|
+
capabilitySummary: agent.capabilitySummary ?? current?.capabilitySummary,
|
|
34
|
+
capabilitySources: agent.capabilitySources ?? current?.capabilitySources,
|
|
35
|
+
capabilityProfile: agent.capabilityProfile ?? current?.capabilityProfile,
|
|
36
|
+
}),
|
|
37
|
+
bootstrapStatus: agent.bootstrapStatus ?? current?.bootstrapStatus ?? "ready",
|
|
38
|
+
registrationSource: agent.registrationSource ?? current?.registrationSource ?? "unknown",
|
|
39
|
+
registrationStatus: agent.registrationStatus ?? current?.registrationStatus ?? "confirmed",
|
|
40
|
+
lastRegistrationAttemptAt: agent.lastRegistrationAttemptAt ?? current?.lastRegistrationAttemptAt,
|
|
41
|
+
confirmedByAgentAt: agent.confirmedByAgentAt ?? current?.confirmedByAgentAt,
|
|
42
|
+
sessionFingerprint: agent.sessionFingerprint ?? current?.sessionFingerprint ?? agent.sessionId,
|
|
43
|
+
registrationHistory: appendRegistrationHistory(current?.registrationHistory, agent, canonicalId),
|
|
44
|
+
registrationWarnings,
|
|
45
|
+
permissionProfile: agent.permissionProfile ?? current?.permissionProfile ?? "default",
|
|
46
|
+
permissionProfileStatus: agent.permissionProfileStatus ?? current?.permissionProfileStatus ?? "not-requested",
|
|
47
|
+
permissionProfileDetail: agent.permissionProfileDetail ?? current?.permissionProfileDetail,
|
|
48
|
+
mcpPreflightStatus: agent.mcpPreflightStatus ?? current?.mcpPreflightStatus,
|
|
49
|
+
mcpPreflightDetail: agent.mcpPreflightDetail ?? current?.mcpPreflightDetail,
|
|
50
|
+
mcpSetupCommand: agent.mcpSetupCommand ?? current?.mcpSetupCommand,
|
|
51
|
+
mcpVerifyCommand: agent.mcpVerifyCommand ?? current?.mcpVerifyCommand,
|
|
52
|
+
mcpRemoveCommand: agent.mcpRemoveCommand ?? current?.mcpRemoveCommand,
|
|
53
|
+
mcpUrl: agent.mcpUrl ?? current?.mcpUrl,
|
|
54
|
+
lastSeenAt: agent.lastSeenAt ?? current?.lastSeenAt,
|
|
55
|
+
lastActivityAt: agent.lastActivityAt ?? current?.lastActivityAt,
|
|
56
|
+
activeRequestId: valueOrCurrent(agent, current, "activeRequestId"),
|
|
57
|
+
leaseStatus: valueOrCurrent(agent, current, "leaseStatus"),
|
|
58
|
+
leaseStartedAt: valueOrCurrent(agent, current, "leaseStartedAt"),
|
|
59
|
+
leaseExpiresAt: valueOrCurrent(agent, current, "leaseExpiresAt"),
|
|
60
|
+
leaseReleasedAt: valueOrCurrent(agent, current, "leaseReleasedAt"),
|
|
61
|
+
};
|
|
62
|
+
if ((agent.capabilitySummary !== undefined || agent.capabilitySources !== undefined)
|
|
63
|
+
&& (options.capabilitySource !== undefined || options.refreshedAt !== undefined)) {
|
|
64
|
+
const timestamp = options.refreshedAt ?? new Date().toISOString();
|
|
65
|
+
next.capabilitySource = options.capabilitySource ?? agent.capabilitySource ?? current?.capabilitySource;
|
|
66
|
+
next.capabilityUpdatedAt = timestamp;
|
|
67
|
+
if ((options.capabilitySource ?? agent.capabilitySource) === "generated") {
|
|
68
|
+
next.lastCapabilityRefreshAt = timestamp;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
this.agents.set(canonicalId, next);
|
|
72
|
+
return next;
|
|
14
73
|
}
|
|
15
74
|
list() {
|
|
16
75
|
return Array.from(this.agents.values()).sort((a, b) => a.id.localeCompare(b.id));
|
|
@@ -18,6 +77,9 @@ class AgentRegistry {
|
|
|
18
77
|
get(id) {
|
|
19
78
|
return this.agents.get(id);
|
|
20
79
|
}
|
|
80
|
+
findBySessionFingerprint(sessionFingerprint) {
|
|
81
|
+
return this.list().find((agent) => agent.sessionFingerprint === sessionFingerprint);
|
|
82
|
+
}
|
|
21
83
|
unregister(id) {
|
|
22
84
|
const agent = this.require(id);
|
|
23
85
|
this.agents.delete(id);
|
|
@@ -30,5 +92,68 @@ class AgentRegistry {
|
|
|
30
92
|
}
|
|
31
93
|
return agent;
|
|
32
94
|
}
|
|
95
|
+
updateCapabilities(id, update) {
|
|
96
|
+
const agent = this.require(id);
|
|
97
|
+
const timestamp = update.refreshedAt ?? new Date().toISOString();
|
|
98
|
+
const next = {
|
|
99
|
+
...agent,
|
|
100
|
+
capabilitySummary: update.summary ?? agent.capabilitySummary,
|
|
101
|
+
capabilitySources: update.sources ?? agent.capabilitySources,
|
|
102
|
+
capabilitySource: update.source,
|
|
103
|
+
capabilityUpdatedAt: timestamp,
|
|
104
|
+
lastCapabilityRefreshAt: update.source === "generated" ? timestamp : agent.lastCapabilityRefreshAt,
|
|
105
|
+
capabilityProfile: (0, capability_profile_1.buildCapabilityProfile)({
|
|
106
|
+
capabilitySummary: update.summary ?? agent.capabilitySummary,
|
|
107
|
+
capabilitySources: update.sources ?? agent.capabilitySources,
|
|
108
|
+
capabilityProfile: update.profile ?? agent.capabilityProfile,
|
|
109
|
+
}),
|
|
110
|
+
};
|
|
111
|
+
this.agents.set(id, next);
|
|
112
|
+
return next;
|
|
113
|
+
}
|
|
114
|
+
updateHealth(id, update) {
|
|
115
|
+
const agent = this.require(id);
|
|
116
|
+
const next = {
|
|
117
|
+
...agent,
|
|
118
|
+
lastSeenAt: update.lastSeenAt ?? agent.lastSeenAt,
|
|
119
|
+
lastActivityAt: update.lastActivityAt ?? agent.lastActivityAt,
|
|
120
|
+
};
|
|
121
|
+
this.agents.set(id, next);
|
|
122
|
+
return next;
|
|
123
|
+
}
|
|
33
124
|
}
|
|
34
125
|
exports.AgentRegistry = AgentRegistry;
|
|
126
|
+
function appendRegistrationHistory(current = [], agent, canonicalId) {
|
|
127
|
+
const at = agent.confirmedByAgentAt ?? agent.lastRegistrationAttemptAt;
|
|
128
|
+
const source = agent.registrationSource;
|
|
129
|
+
const status = agent.registrationStatus;
|
|
130
|
+
if (!at || !source || !status) {
|
|
131
|
+
return current;
|
|
132
|
+
}
|
|
133
|
+
const event = {
|
|
134
|
+
at,
|
|
135
|
+
source,
|
|
136
|
+
status,
|
|
137
|
+
agentId: canonicalId,
|
|
138
|
+
sessionFingerprint: agent.sessionFingerprint,
|
|
139
|
+
};
|
|
140
|
+
const last = current[current.length - 1];
|
|
141
|
+
if (last
|
|
142
|
+
&& last.at === event.at
|
|
143
|
+
&& last.source === event.source
|
|
144
|
+
&& last.status === event.status
|
|
145
|
+
&& last.agentId === event.agentId
|
|
146
|
+
&& last.sessionFingerprint === event.sessionFingerprint) {
|
|
147
|
+
return current;
|
|
148
|
+
}
|
|
149
|
+
return [...current, event];
|
|
150
|
+
}
|
|
151
|
+
function mergeRegistrationWarnings(current = [], next) {
|
|
152
|
+
if (!next) {
|
|
153
|
+
return current.length ? current : undefined;
|
|
154
|
+
}
|
|
155
|
+
return current.includes(next) ? current : [...current, next];
|
|
156
|
+
}
|
|
157
|
+
function valueOrCurrent(agent, current, key) {
|
|
158
|
+
return Object.prototype.hasOwnProperty.call(agent, key) ? agent[key] : current?.[key];
|
|
159
|
+
}
|