@minasoft/mina-ai-router 0.1.5 → 0.2.0
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 +67 -16
- package/dist/apps/cli/src/index.js +1247 -43
- package/dist/apps/http-server/src/index.js +558 -42
- 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 +54 -7
- package/dist/packages/core/src/capability-profile.js +145 -0
- package/dist/packages/core/src/index.js +3 -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/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 +47 -0
- package/docs/USER-START-GUIDE.md +155 -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>
|
|
@@ -7,7 +7,7 @@ const node_fs_1 = require("node:fs");
|
|
|
7
7
|
const src_1 = require("../../../packages/core/src");
|
|
8
8
|
const src_2 = require("../../../packages/transports/src");
|
|
9
9
|
const importEsm = new Function("specifier", "return import(specifier)");
|
|
10
|
-
const runtimeModuleUrl = (0, node_url_1.pathToFileURL)(
|
|
10
|
+
const runtimeModuleUrl = (0, node_url_1.pathToFileURL)(require.resolve("@minasoft/mcp-runtime")).href;
|
|
11
11
|
const statePath = process.env.MINA_ROUTER_STATE ?? (0, node_path_1.join)(process.cwd(), "data", "router-state.json");
|
|
12
12
|
const logPath = process.env.MINA_MCP_LOG;
|
|
13
13
|
const context = createContext();
|
|
@@ -102,7 +102,11 @@ async function createProvider() {
|
|
|
102
102
|
description: "List registered Mina helper agents.",
|
|
103
103
|
inputSchema: {
|
|
104
104
|
type: "object",
|
|
105
|
-
properties: {
|
|
105
|
+
properties: {
|
|
106
|
+
callerAgentId: { type: "string" },
|
|
107
|
+
sourceAgent: { type: "string" },
|
|
108
|
+
callerSessionFingerprint: { type: "string" },
|
|
109
|
+
},
|
|
106
110
|
additionalProperties: false,
|
|
107
111
|
},
|
|
108
112
|
},
|
|
@@ -117,11 +121,15 @@ async function createProvider() {
|
|
|
117
121
|
agentType: { type: "string" },
|
|
118
122
|
transport: { type: "string" },
|
|
119
123
|
sessionId: { type: "string" },
|
|
124
|
+
sessionFingerprint: { type: "string" },
|
|
120
125
|
projectRoot: { type: "string" },
|
|
121
126
|
tmuxTarget: { type: "string" },
|
|
122
127
|
startupCommand: { type: "string" },
|
|
123
128
|
capabilitySummary: { type: "string" },
|
|
124
129
|
capabilitySources: { type: "string" },
|
|
130
|
+
capabilitySource: { type: "string" },
|
|
131
|
+
capabilityUpdatedAt: { type: "string" },
|
|
132
|
+
lastCapabilityRefreshAt: { type: "string" },
|
|
125
133
|
},
|
|
126
134
|
required: ["id", "agentType", "transport", "sessionId", "projectRoot"],
|
|
127
135
|
additionalProperties: false,
|
|
@@ -135,6 +143,10 @@ async function createProvider() {
|
|
|
135
143
|
properties: {
|
|
136
144
|
target: { type: "string" },
|
|
137
145
|
task: { type: "string" },
|
|
146
|
+
sourceAgent: { type: "string" },
|
|
147
|
+
callerAgentId: { type: "string" },
|
|
148
|
+
callerSessionFingerprint: { type: "string" },
|
|
149
|
+
allowSelfCall: { type: "boolean" },
|
|
138
150
|
timeoutMs: { type: "number" },
|
|
139
151
|
},
|
|
140
152
|
required: ["target", "task"],
|
|
@@ -157,7 +169,7 @@ async function createProvider() {
|
|
|
157
169
|
providerPromise = Promise.resolve({
|
|
158
170
|
serverInfo: {
|
|
159
171
|
name: "mina-ai-router",
|
|
160
|
-
version:
|
|
172
|
+
version: (0, src_1.packageVersion)(),
|
|
161
173
|
},
|
|
162
174
|
tools: {
|
|
163
175
|
async list() {
|
|
@@ -173,16 +185,28 @@ async function createProvider() {
|
|
|
173
185
|
async function callTool(name, args) {
|
|
174
186
|
switch (name) {
|
|
175
187
|
case "list_agents": {
|
|
176
|
-
const
|
|
188
|
+
const caller = resolveCallerAgent(args);
|
|
189
|
+
const agents = await context.router.listAgentStatuses({ callerAgentId: caller?.id });
|
|
177
190
|
return jsonToolResult({ agents });
|
|
178
191
|
}
|
|
179
192
|
case "register_agent": {
|
|
180
193
|
try {
|
|
181
194
|
const agent = agentFromArgs(args);
|
|
182
|
-
|
|
195
|
+
const now = new Date().toISOString();
|
|
196
|
+
const registered = context.registry.register({
|
|
197
|
+
...agent,
|
|
198
|
+
bootstrapStatus: "ready",
|
|
199
|
+
registrationSource: "mcp",
|
|
200
|
+
registrationStatus: "confirmed",
|
|
201
|
+
lastRegistrationAttemptAt: now,
|
|
202
|
+
confirmedByAgentAt: now,
|
|
203
|
+
sessionFingerprint: agent.sessionFingerprint ?? agent.sessionId,
|
|
204
|
+
}, {
|
|
205
|
+
capabilitySource: agent.capabilitySummary || agent.capabilitySources ? "generated" : undefined,
|
|
206
|
+
});
|
|
183
207
|
context.save();
|
|
184
208
|
const agents = await context.router.listAgentStatuses();
|
|
185
|
-
return jsonToolResult({ agent, agents });
|
|
209
|
+
return jsonToolResult({ agent: registered, agents });
|
|
186
210
|
}
|
|
187
211
|
catch (error) {
|
|
188
212
|
return {
|
|
@@ -195,11 +219,19 @@ async function callTool(name, args) {
|
|
|
195
219
|
const target = typeof args.target === "string" ? args.target : "";
|
|
196
220
|
const task = typeof args.task === "string" ? args.task : "";
|
|
197
221
|
const timeoutMs = typeof args.timeoutMs === "number" ? args.timeoutMs : undefined;
|
|
222
|
+
const caller = resolveCallerAgent(args);
|
|
223
|
+
const allowSelfCall = args.allowSelfCall === true;
|
|
198
224
|
if (!target || !task) {
|
|
199
225
|
return { kind: "invalid_params", message: "call_agent requires string target and task." };
|
|
200
226
|
}
|
|
201
227
|
try {
|
|
202
|
-
const response = await context.router.callAgent({
|
|
228
|
+
const response = await context.router.callAgent({
|
|
229
|
+
sourceAgent: caller?.id,
|
|
230
|
+
target,
|
|
231
|
+
task,
|
|
232
|
+
timeoutMs,
|
|
233
|
+
allowSelfCall,
|
|
234
|
+
});
|
|
203
235
|
return jsonToolResult(response);
|
|
204
236
|
}
|
|
205
237
|
catch (error) {
|
|
@@ -235,6 +267,14 @@ async function callTool(name, args) {
|
|
|
235
267
|
return { kind: "not_found", message: `Unknown tool "${name}".` };
|
|
236
268
|
}
|
|
237
269
|
}
|
|
270
|
+
function resolveCallerAgent(args) {
|
|
271
|
+
const callerAgentId = stringValue(args.callerAgentId) ?? stringValue(args.sourceAgent);
|
|
272
|
+
if (callerAgentId) {
|
|
273
|
+
return context.registry.get(callerAgentId);
|
|
274
|
+
}
|
|
275
|
+
const fingerprint = stringValue(args.callerSessionFingerprint);
|
|
276
|
+
return fingerprint ? context.registry.findBySessionFingerprint(fingerprint) : undefined;
|
|
277
|
+
}
|
|
238
278
|
function agentFromArgs(args) {
|
|
239
279
|
const id = requiredString(args.id, "id");
|
|
240
280
|
return {
|
|
@@ -243,11 +283,15 @@ function agentFromArgs(args) {
|
|
|
243
283
|
agentType: requiredString(args.agentType, "agentType"),
|
|
244
284
|
transport: requiredString(args.transport, "transport"),
|
|
245
285
|
sessionId: requiredString(args.sessionId, "sessionId"),
|
|
286
|
+
sessionFingerprint: stringValue(args.sessionFingerprint) ?? stringValue(args.sessionId),
|
|
246
287
|
projectRoot: requiredString(args.projectRoot, "projectRoot"),
|
|
247
288
|
tmuxTarget: stringValue(args.tmuxTarget),
|
|
248
289
|
startupCommand: stringValue(args.startupCommand),
|
|
249
290
|
capabilitySummary: stringValue(args.capabilitySummary),
|
|
250
291
|
capabilitySources: stringValue(args.capabilitySources),
|
|
292
|
+
capabilitySource: capabilitySourceValue(args.capabilitySource),
|
|
293
|
+
capabilityUpdatedAt: stringValue(args.capabilityUpdatedAt),
|
|
294
|
+
lastCapabilityRefreshAt: stringValue(args.lastCapabilityRefreshAt),
|
|
251
295
|
};
|
|
252
296
|
}
|
|
253
297
|
function requiredString(value, field) {
|
|
@@ -259,6 +303,9 @@ function requiredString(value, field) {
|
|
|
259
303
|
function stringValue(value) {
|
|
260
304
|
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
261
305
|
}
|
|
306
|
+
function capabilitySourceValue(value) {
|
|
307
|
+
return value === "manual" || value === "generated" ? value : undefined;
|
|
308
|
+
}
|
|
262
309
|
function jsonToolResult(value) {
|
|
263
310
|
const text = JSON.stringify(value, null, 2);
|
|
264
311
|
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,13 @@ 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);
|
|
24
26
|
__exportStar(require("./types"), exports);
|
|
27
|
+
__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
|
+
}
|