@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.
Files changed (31) hide show
  1. package/README.md +69 -16
  2. package/dist/apps/cli/src/index.js +1251 -46
  3. package/dist/apps/http-server/src/index.js +559 -43
  4. package/dist/apps/http-server/src/public/assets/index-Bl059Jd0.js +9 -0
  5. package/dist/apps/http-server/src/public/assets/index-CaPxN_Ez.css +1 -0
  6. package/dist/apps/http-server/src/public/index.html +2 -2
  7. package/dist/apps/mcp-server/src/index.js +55 -9
  8. package/dist/packages/core/src/capability-profile.js +145 -0
  9. package/dist/packages/core/src/index.js +4 -0
  10. package/dist/packages/core/src/mcp-preflight.js +80 -0
  11. package/dist/packages/core/src/registry.js +128 -3
  12. package/dist/packages/core/src/request-store.js +158 -0
  13. package/dist/packages/core/src/response-parser.js +76 -8
  14. package/dist/packages/core/src/router.js +408 -13
  15. package/dist/packages/core/src/runtime-paths.js +16 -0
  16. package/dist/packages/core/src/version.js +57 -0
  17. package/dist/packages/mcp/src/provider.js +57 -6
  18. package/dist/packages/transports/src/headless/headless-transport.js +13 -8
  19. package/dist/packages/transports/src/tmux/tmux-client.js +334 -0
  20. package/dist/packages/transports/src/tmux/tmux-transport.js +10 -0
  21. package/docs/DEVELOPER-START-GUIDE.md +9 -1
  22. package/docs/GETTING-STARTED.md +10 -5
  23. package/docs/HTTP-UI-MCP.md +39 -13
  24. package/docs/MCP-CLIENT-SETUP.md +56 -3
  25. package/docs/SKILL-INSTALL-GUIDE.md +21 -3
  26. package/docs/TROUBLESHOOTING.md +51 -2
  27. package/docs/USER-START-GUIDE.md +157 -26
  28. package/docs/assets/mina-ai-router-overview.svg +109 -0
  29. package/package.json +8 -2
  30. package/dist/apps/http-server/src/public/assets/index-Be0tne90.js +0 -9
  31. 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-Be0tne90.js"></script>
11
- <link rel="stylesheet" crossorigin href="/assets/index-CEhd8YGG.css">
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)((0, node_path_1.join)(__dirname, "../../../../node_modules/@minasoft/mcp-runtime/dist/index.js")).href;
11
- const statePath = process.env.MINA_ROUTER_STATE ?? (0, node_path_1.join)(process.cwd(), "data", "router-state.json");
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: "0.1.0",
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 agents = await context.router.listAgentStatuses();
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
- context.registry.register(agent);
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({ target, task, timeoutMs });
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
- this.agents.set(agent.id, agent);
13
- return agent;
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
+ }