@jmanuelcorral/openteam 0.1.38 → 0.1.40
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/.opencode/command/openteam.md +5 -5
- package/AGENTS.md +27 -27
- package/README.es.md +759 -0
- package/README.md +213 -480
- package/dist/capabilities/types.d.ts +1 -1
- package/dist/cli/consoleServe.d.ts.map +1 -1
- package/dist/cli.js +1085 -231
- package/dist/commands/frontierCatalog.d.ts +27 -0
- package/dist/commands/frontierCatalog.d.ts.map +1 -0
- package/dist/commands/orchestratorAgent.d.ts +2 -1
- package/dist/commands/orchestratorAgent.d.ts.map +1 -1
- package/dist/commands/setup.d.ts +3 -18
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/config/schema.d.ts +196 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/console/assets.d.ts +9 -0
- package/dist/console/assets.d.ts.map +1 -0
- package/dist/console/render.d.ts.map +1 -1
- package/dist/contract/opencode.d.ts +1 -1
- package/dist/contract/opencode.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +835 -87
- package/dist/local/embeddings.d.ts +29 -0
- package/dist/local/embeddings.d.ts.map +1 -0
- package/dist/local/extractClient.d.ts +31 -0
- package/dist/local/extractClient.d.ts.map +1 -0
- package/dist/local/openai-compatible.d.ts +1 -0
- package/dist/local/openai-compatible.d.ts.map +1 -1
- package/dist/mcp/semanticRecall.d.ts +25 -0
- package/dist/mcp/semanticRecall.d.ts.map +1 -0
- package/dist/mcp/server.d.ts +22 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/memory/consolidate.d.ts +21 -0
- package/dist/memory/consolidate.d.ts.map +1 -0
- package/dist/memory/extract.d.ts +68 -0
- package/dist/memory/extract.d.ts.map +1 -0
- package/dist/memory/fold.d.ts +24 -0
- package/dist/memory/fold.d.ts.map +1 -0
- package/dist/memory/index.d.ts +18 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/inject.d.ts +29 -0
- package/dist/memory/inject.d.ts.map +1 -0
- package/dist/memory/log.d.ts +16 -0
- package/dist/memory/log.d.ts.map +1 -0
- package/dist/memory/merge.d.ts +38 -0
- package/dist/memory/merge.d.ts.map +1 -0
- package/dist/memory/rank.d.ts +32 -0
- package/dist/memory/rank.d.ts.map +1 -0
- package/dist/memory/recall.d.ts +30 -0
- package/dist/memory/recall.d.ts.map +1 -0
- package/dist/memory/temporal.d.ts +39 -0
- package/dist/memory/temporal.d.ts.map +1 -0
- package/dist/memory/types.d.ts +159 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/vector.d.ts +18 -0
- package/dist/memory/vector.d.ts.map +1 -0
- package/dist/orchestrator/roles.d.ts +1 -1
- package/dist/orchestrator/roles.d.ts.map +1 -1
- package/dist/plugin/hooks.d.ts +13 -0
- package/dist/plugin/hooks.d.ts.map +1 -1
- package/dist/plugin/memoryInject.d.ts +25 -0
- package/dist/plugin/memoryInject.d.ts.map +1 -0
- package/dist/router/chooseModel.d.ts.map +1 -1
- package/dist/router/cost.d.ts +4 -0
- package/dist/router/cost.d.ts.map +1 -0
- package/dist/router/frontierBaseline.d.ts +13 -0
- package/dist/router/frontierBaseline.d.ts.map +1 -0
- package/dist/router/modelSelection.d.ts +18 -0
- package/dist/router/modelSelection.d.ts.map +1 -0
- package/dist/storage/index/memoryConsolidate.d.ts +24 -0
- package/dist/storage/index/memoryConsolidate.d.ts.map +1 -0
- package/dist/storage/index/memoryIndex.d.ts +52 -0
- package/dist/storage/index/memoryIndex.d.ts.map +1 -0
- package/dist/storage/index/memoryRecall.d.ts +23 -0
- package/dist/storage/index/memoryRecall.d.ts.map +1 -0
- package/dist/storage/index/memoryRuntime.d.ts +41 -0
- package/dist/storage/index/memoryRuntime.d.ts.map +1 -0
- package/dist/telemetry/events.d.ts +3 -1
- package/dist/telemetry/events.d.ts.map +1 -1
- package/dist/telemetry/hash.d.ts.map +1 -1
- package/dist/web/server.d.ts +4 -0
- package/dist/web/server.d.ts.map +1 -1
- package/package.json +14 -6
package/dist/cli.js
CHANGED
|
@@ -674,6 +674,50 @@ var LocalRuntimeSchema = z3.object({
|
|
|
674
674
|
discovery: z3.enum(["cli", "sdk", "manual"]).optional(),
|
|
675
675
|
defaultModel: ModelRefSchema
|
|
676
676
|
});
|
|
677
|
+
var MemoryScopeSchema = z3.enum(["project", "user"]);
|
|
678
|
+
var MemoryExtractionModeSchema = z3.enum([
|
|
679
|
+
"local",
|
|
680
|
+
"localWithFrontierFallback"
|
|
681
|
+
]);
|
|
682
|
+
var MemoryEmbeddingsProviderSchema = z3.enum(["local", "none"]);
|
|
683
|
+
var MemorySemanticSchema = z3.object({
|
|
684
|
+
enabled: z3.boolean().default(false),
|
|
685
|
+
storeContent: z3.boolean().default(false),
|
|
686
|
+
logPath: z3.string().min(1).default(".opencode/openteam/memory/records.jsonl"),
|
|
687
|
+
indexPath: z3.string().min(1).default(".opencode/openteam/memory/index.sqlite"),
|
|
688
|
+
scope: MemoryScopeSchema.default("project"),
|
|
689
|
+
extraction: z3.object({
|
|
690
|
+
mode: MemoryExtractionModeSchema.default("local"),
|
|
691
|
+
model: ModelRefSchema.nullable().default(null)
|
|
692
|
+
}).default({ mode: "local", model: null }),
|
|
693
|
+
embeddings: z3.object({
|
|
694
|
+
provider: MemoryEmbeddingsProviderSchema.default("local"),
|
|
695
|
+
model: ModelRefSchema.nullable().default(null),
|
|
696
|
+
dim: z3.number().int().positive().default(768)
|
|
697
|
+
}).default({ provider: "local", model: null, dim: 768 }),
|
|
698
|
+
recall: z3.object({
|
|
699
|
+
maxResults: z3.number().int().positive().default(8),
|
|
700
|
+
minSimilarity: z3.number().min(0).max(1).default(0.2)
|
|
701
|
+
}).default({ maxResults: 8, minSimilarity: 0.2 }),
|
|
702
|
+
injection: z3.object({
|
|
703
|
+
enabled: z3.boolean().default(false),
|
|
704
|
+
maxChars: z3.number().int().positive().default(1200),
|
|
705
|
+
maxItems: z3.number().int().positive().default(8)
|
|
706
|
+
}).default({ enabled: false, maxChars: 1200, maxItems: 8 })
|
|
707
|
+
}).default({
|
|
708
|
+
enabled: false,
|
|
709
|
+
storeContent: false,
|
|
710
|
+
logPath: ".opencode/openteam/memory/records.jsonl",
|
|
711
|
+
indexPath: ".opencode/openteam/memory/index.sqlite",
|
|
712
|
+
scope: "project",
|
|
713
|
+
extraction: { mode: "local", model: null },
|
|
714
|
+
embeddings: { provider: "local", model: null, dim: 768 },
|
|
715
|
+
recall: { maxResults: 8, minSimilarity: 0.2 },
|
|
716
|
+
injection: { enabled: false, maxChars: 1200, maxItems: 8 }
|
|
717
|
+
});
|
|
718
|
+
var MemoryConfigSchema = z3.object({
|
|
719
|
+
semantic: MemorySemanticSchema
|
|
720
|
+
});
|
|
677
721
|
var OpenTeamConfigObjectSchema = z3.object({
|
|
678
722
|
baseline: z3.object({
|
|
679
723
|
mode: BaselineModeSchema.default("auto"),
|
|
@@ -724,9 +768,13 @@ var OpenTeamConfigObjectSchema = z3.object({
|
|
|
724
768
|
}).default({ hardStopOnBudgetExhaustion: false }),
|
|
725
769
|
privacyMode: PrivacyModeSchema.default("forceLocalOnSensitive"),
|
|
726
770
|
console: ConsoleConfigSchema,
|
|
727
|
-
storage: StorageConfigSchema
|
|
771
|
+
storage: StorageConfigSchema,
|
|
772
|
+
memory: MemoryConfigSchema.optional()
|
|
728
773
|
});
|
|
729
774
|
var OpenTeamConfigSchema = OpenTeamConfigObjectSchema;
|
|
775
|
+
function resolveMemorySemantic(config) {
|
|
776
|
+
return config.memory?.semantic ?? MemorySemanticSchema.parse(undefined);
|
|
777
|
+
}
|
|
730
778
|
|
|
731
779
|
// src/telemetry/events.ts
|
|
732
780
|
var EVENT_SCHEMA_VERSION = 1;
|
|
@@ -1182,6 +1230,208 @@ import {
|
|
|
1182
1230
|
createServer as createHttpServer
|
|
1183
1231
|
} from "node:http";
|
|
1184
1232
|
|
|
1233
|
+
// src/console/assets.ts
|
|
1234
|
+
var CONSOLE_STYLE = `
|
|
1235
|
+
:root{color-scheme:dark;--bg:#0f1115;--panel:#171a21;--fg:#e6e8eb;--muted:#8b929c;--good:#3fb950;--local:#58a6ff;--frontier:#d29922;--line:#262b34}
|
|
1236
|
+
*{box-sizing:border-box}
|
|
1237
|
+
body{margin:0;background:var(--bg);color:var(--fg);font:14px/1.5 ui-sans-serif,system-ui,Segoe UI,Roboto,sans-serif}
|
|
1238
|
+
header{padding:16px 24px;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;align-items:baseline;gap:12px;flex-wrap:wrap}
|
|
1239
|
+
header h1{font-size:18px;margin:0}
|
|
1240
|
+
header .meta{color:var(--muted);font-size:12px}
|
|
1241
|
+
main{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:16px;padding:24px}
|
|
1242
|
+
.panel{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:16px}
|
|
1243
|
+
.panel h2{font-size:14px;margin:0 0 12px;color:var(--muted);text-transform:uppercase;letter-spacing:.04em}
|
|
1244
|
+
.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px}
|
|
1245
|
+
.stat{display:flex;flex-direction:column;gap:2px}
|
|
1246
|
+
.stat .k{color:var(--muted);font-size:12px}
|
|
1247
|
+
.stat .v{font-size:18px;font-weight:600}
|
|
1248
|
+
.good{color:var(--good)}
|
|
1249
|
+
.local{color:var(--local)}
|
|
1250
|
+
.frontier{color:var(--frontier)}
|
|
1251
|
+
.muted{color:var(--muted)}
|
|
1252
|
+
table{width:100%;border-collapse:collapse;font-size:13px}
|
|
1253
|
+
th,td{text-align:left;padding:6px 8px;border-bottom:1px solid var(--line)}
|
|
1254
|
+
th{color:var(--muted);font-weight:600}
|
|
1255
|
+
.hash{font-family:ui-monospace,Consolas,monospace;color:var(--muted)}
|
|
1256
|
+
.progress{height:10px;background:#0b0d11;border-radius:6px;overflow:hidden;border:1px solid var(--line)}
|
|
1257
|
+
.progress .bar{height:100%;background:var(--good)}
|
|
1258
|
+
ul.items,ul.timeline{list-style:none;margin:8px 0 0;padding:0;max-height:320px;overflow:auto}
|
|
1259
|
+
ul.items li,ul.timeline li{padding:4px 0;border-bottom:1px solid var(--line)}
|
|
1260
|
+
ul.items li.done{color:var(--muted)}
|
|
1261
|
+
ul.items .box{font-family:monospace}
|
|
1262
|
+
.who{color:var(--local)}
|
|
1263
|
+
.tag{color:var(--frontier);font-size:12px}
|
|
1264
|
+
.kind{display:inline-block;min-width:66px;color:var(--muted);font-size:12px}
|
|
1265
|
+
code{font-family:ui-monospace,Consolas,monospace;background:#0b0d11;padding:1px 5px;border-radius:4px}
|
|
1266
|
+
.mono{font-family:ui-monospace,Consolas,monospace;font-size:12px}
|
|
1267
|
+
.tabs{display:flex;gap:6px;padding:12px 24px 0;flex-wrap:wrap;border-bottom:1px solid var(--line)}
|
|
1268
|
+
.tabs .tab{background:var(--panel);color:var(--fg);border:1px solid var(--line);border-bottom:none;border-radius:8px 8px 0 0;padding:8px 14px;cursor:pointer;font:inherit;display:flex;align-items:center;gap:6px}
|
|
1269
|
+
.tabs .tab.active{background:var(--bg);border-color:var(--local);color:var(--fg)}
|
|
1270
|
+
.dot{width:8px;height:8px;border-radius:50%;display:inline-block;background:var(--muted)}
|
|
1271
|
+
.badge{font-size:11px;padding:1px 8px;border-radius:10px;border:1px solid var(--line);color:var(--muted);vertical-align:middle}
|
|
1272
|
+
.status-running{background:var(--good)}
|
|
1273
|
+
.badge.status-running{color:var(--good);border-color:var(--good)}
|
|
1274
|
+
.status-idle{background:var(--muted)}
|
|
1275
|
+
.badge.status-idle{color:var(--muted)}
|
|
1276
|
+
.status-waiting-input{background:var(--frontier)}
|
|
1277
|
+
.badge.status-waiting-input{color:var(--frontier);border-color:var(--frontier)}
|
|
1278
|
+
.status-ended{background:#6e7681}
|
|
1279
|
+
.badge.status-ended{color:#6e7681}
|
|
1280
|
+
.term .termlog{background:#0b0d11;border:1px solid var(--line);border-radius:8px;padding:10px;max-height:340px;overflow:auto;font-family:ui-monospace,Consolas,monospace;font-size:12px;white-space:pre-wrap}
|
|
1281
|
+
.term .tl{padding:1px 0}
|
|
1282
|
+
.term .tl.input{color:var(--local)}
|
|
1283
|
+
.term .tl.tool{color:var(--frontier)}
|
|
1284
|
+
.term .tl.status{color:var(--muted)}
|
|
1285
|
+
.term .tl.error{color:#f85149}
|
|
1286
|
+
.term .tl.permission{color:var(--frontier);font-weight:600}
|
|
1287
|
+
.term form.terminput{display:flex;gap:8px;margin-top:10px}
|
|
1288
|
+
.term form.terminput input{flex:1;background:#0b0d11;color:var(--fg);border:1px solid var(--line);border-radius:8px;padding:8px 10px;font:inherit}
|
|
1289
|
+
.term form.terminput button{background:var(--local);color:#04122b;border:none;border-radius:8px;padding:8px 16px;cursor:pointer;font:inherit;font-weight:600}
|
|
1290
|
+
.pty .ptylog{background:#05070a;border:1px solid var(--line);border-radius:8px;padding:10px;max-height:340px;overflow:auto;font-family:ui-monospace,Consolas,monospace;font-size:12px;white-space:pre-wrap;color:#c8d1dc}
|
|
1291
|
+
.pty form.ptyinput{display:flex;gap:8px;margin-top:10px}
|
|
1292
|
+
.pty form.ptyinput input{flex:1;background:#05070a;color:var(--fg);border:1px solid var(--line);border-radius:8px;padding:8px 10px;font:inherit}
|
|
1293
|
+
.pty form.ptyinput button,.pty .pty-start{background:var(--frontier);color:#1a1200;border:none;border-radius:8px;padding:8px 16px;cursor:pointer;font:inherit;font-weight:600;margin-top:8px}
|
|
1294
|
+
.pty .pty-start:disabled{opacity:.5;cursor:default}
|
|
1295
|
+
`;
|
|
1296
|
+
var CONSOLE_CLIENT_JS = `
|
|
1297
|
+
(function(){
|
|
1298
|
+
var authTok='';
|
|
1299
|
+
try{
|
|
1300
|
+
var qp=new URLSearchParams(window.location.search).get('token');
|
|
1301
|
+
if(qp){authTok='?token='+encodeURIComponent(qp)}
|
|
1302
|
+
}catch(e){/* ignore */}
|
|
1303
|
+
function reloadIfChanged(prev){
|
|
1304
|
+
fetch('/api/state'+authTok).then(function(r){return r.json()}).then(function(s){
|
|
1305
|
+
if(s.generatedAt!==prev){location.reload()}
|
|
1306
|
+
}).catch(function(){});
|
|
1307
|
+
}
|
|
1308
|
+
var current=document.documentElement.getAttribute('data-generated')||'';
|
|
1309
|
+
if('EventSource' in window){
|
|
1310
|
+
try{
|
|
1311
|
+
var es=new EventSource('/events'+authTok);
|
|
1312
|
+
es.addEventListener('state',function(){location.reload()});
|
|
1313
|
+
es.onerror=function(){/* fallback below */};
|
|
1314
|
+
}catch(e){/* ignore */}
|
|
1315
|
+
}
|
|
1316
|
+
var ms=parseInt(document.documentElement.getAttribute('data-refresh')||'2000',10);
|
|
1317
|
+
setInterval(function(){reloadIfChanged(current)},isNaN(ms)?2000:Math.max(250,ms));
|
|
1318
|
+
|
|
1319
|
+
function selectTab(name){
|
|
1320
|
+
var tabs=document.querySelectorAll('#tabbar .tab');
|
|
1321
|
+
for(var i=0;i<tabs.length;i++){tabs[i].classList.toggle('active',tabs[i].getAttribute('data-tab')===name)}
|
|
1322
|
+
var views=document.querySelectorAll('main .view');
|
|
1323
|
+
var matched=false;
|
|
1324
|
+
for(var j=0;j<views.length;j++){
|
|
1325
|
+
var show=views[j].getAttribute('data-view')===name;
|
|
1326
|
+
views[j].hidden=!show;
|
|
1327
|
+
if(show){matched=true;if(name!=='all'){connectTerminal(views[j],name)}}
|
|
1328
|
+
}
|
|
1329
|
+
if(!matched){return false}
|
|
1330
|
+
try{sessionStorage.setItem('openteam.tab',name)}catch(e){}
|
|
1331
|
+
return true;
|
|
1332
|
+
}
|
|
1333
|
+
function connectTerminal(view,id){
|
|
1334
|
+
if(view.__wired){return}
|
|
1335
|
+
var root=document.documentElement;
|
|
1336
|
+
var token=root.getAttribute('data-token');
|
|
1337
|
+
if(root.getAttribute('data-terminal')!=='1'||!token){return}
|
|
1338
|
+
var log=view.querySelector('.termlog');
|
|
1339
|
+
if(!log){return}
|
|
1340
|
+
view.__wired=true;
|
|
1341
|
+
function append(cls,text){
|
|
1342
|
+
var line=document.createElement('div');
|
|
1343
|
+
line.className='tl '+cls;line.textContent=text;
|
|
1344
|
+
log.appendChild(line);log.scrollTop=log.scrollHeight;
|
|
1345
|
+
}
|
|
1346
|
+
var q='/console/session/'+encodeURIComponent(id);
|
|
1347
|
+
var tk='token='+encodeURIComponent(token);
|
|
1348
|
+
if('EventSource' in window){
|
|
1349
|
+
try{
|
|
1350
|
+
var es=new EventSource(q+'/stream?'+tk);
|
|
1351
|
+
es.addEventListener('frame',function(e){try{var f=JSON.parse(e.data);append(f.kind,f.text)}catch(_){}});
|
|
1352
|
+
es.onerror=function(){/* reconecta solo */};
|
|
1353
|
+
}catch(_){/* ignore */}
|
|
1354
|
+
}
|
|
1355
|
+
var form=view.querySelector('form.terminput');
|
|
1356
|
+
if(form){
|
|
1357
|
+
form.addEventListener('submit',function(e){
|
|
1358
|
+
e.preventDefault();
|
|
1359
|
+
var inp=form.querySelector('input[name=text]');
|
|
1360
|
+
var text=inp.value;
|
|
1361
|
+
if(!text){return}
|
|
1362
|
+
inp.value='';append('input','› '+text);
|
|
1363
|
+
fetch(q+'/input?'+tk,{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({text:text})})
|
|
1364
|
+
.then(function(r){if(!r.ok){append('error','✖ envío rechazado ('+r.status+')')}})
|
|
1365
|
+
.catch(function(){append('error','✖ no se pudo enviar')});
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
connectPty(view,id,q,tk);
|
|
1369
|
+
}
|
|
1370
|
+
function connectPty(view,id,q,tk){
|
|
1371
|
+
var root=document.documentElement;
|
|
1372
|
+
if(root.getAttribute('data-pty')!=='1'){return}
|
|
1373
|
+
var pane=view.querySelector('.pty');
|
|
1374
|
+
if(!pane){return}
|
|
1375
|
+
var log=pane.querySelector('.ptylog');
|
|
1376
|
+
var startBtn=pane.querySelector('.pty-start');
|
|
1377
|
+
var input=pane.querySelector('form.ptyinput');
|
|
1378
|
+
function plog(text){
|
|
1379
|
+
var d=document.createElement('span');d.textContent=text;
|
|
1380
|
+
log.appendChild(d);log.scrollTop=log.scrollHeight;
|
|
1381
|
+
}
|
|
1382
|
+
if(!startBtn){return}
|
|
1383
|
+
startBtn.addEventListener('click',function(){
|
|
1384
|
+
startBtn.disabled=true;plog('Abriendo shell…
|
|
1385
|
+
');
|
|
1386
|
+
fetch(q+'/pty?'+tk,{method:'POST',headers:{'content-type':'application/json'},body:'{}'})
|
|
1387
|
+
.then(function(r){return r.ok?r.json():Promise.reject(r.status)})
|
|
1388
|
+
.then(function(info){openPtyWs(info.wsUrl)})
|
|
1389
|
+
.catch(function(s){plog('✖ no se pudo abrir PTY ('+s+')
|
|
1390
|
+
');startBtn.disabled=false});
|
|
1391
|
+
});
|
|
1392
|
+
function openPtyWs(url){
|
|
1393
|
+
try{
|
|
1394
|
+
var ws=new WebSocket(url);
|
|
1395
|
+
ws.binaryType='arraybuffer';
|
|
1396
|
+
ws.onmessage=function(e){
|
|
1397
|
+
var data=e.data;
|
|
1398
|
+
if(data instanceof ArrayBuffer){data=new TextDecoder().decode(new Uint8Array(data))}
|
|
1399
|
+
plog(data);
|
|
1400
|
+
};
|
|
1401
|
+
ws.onclose=function(){plog('
|
|
1402
|
+
[shell cerrada]
|
|
1403
|
+
');startBtn.disabled=false};
|
|
1404
|
+
ws.onerror=function(){plog('
|
|
1405
|
+
✖ error de WebSocket
|
|
1406
|
+
')};
|
|
1407
|
+
if(input){
|
|
1408
|
+
input.hidden=false;
|
|
1409
|
+
input.addEventListener('submit',function(e){
|
|
1410
|
+
e.preventDefault();
|
|
1411
|
+
var c=input.querySelector('input[name=cmd]');
|
|
1412
|
+
if(!c.value){return}
|
|
1413
|
+
ws.send(c.value+'\r');c.value='';
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1416
|
+
}catch(_){plog('
|
|
1417
|
+
✖ WebSocket no disponible
|
|
1418
|
+
');startBtn.disabled=false}
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
var bar=document.getElementById('tabbar');
|
|
1422
|
+
if(bar){
|
|
1423
|
+
bar.addEventListener('click',function(e){
|
|
1424
|
+
var t=e.target;
|
|
1425
|
+
while(t&&t!==bar&&!t.getAttribute('data-tab')){t=t.parentNode}
|
|
1426
|
+
if(t&&t.getAttribute('data-tab')){selectTab(t.getAttribute('data-tab'))}
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
var saved='all';
|
|
1430
|
+
try{saved=sessionStorage.getItem('openteam.tab')||'all'}catch(e){}
|
|
1431
|
+
if(!selectTab(saved)){selectTab('all')}
|
|
1432
|
+
})();
|
|
1433
|
+
`;
|
|
1434
|
+
|
|
1185
1435
|
// src/console/render.ts
|
|
1186
1436
|
var TIERS = [
|
|
1187
1437
|
"trivial",
|
|
@@ -1497,201 +1747,6 @@ function sessionView(tab, terminalEnabled, ptyEnabled) {
|
|
|
1497
1747
|
].join("");
|
|
1498
1748
|
return `<div class="view" data-view="${escapeHtml(tab.sessionID)}" hidden>${body}</div>`;
|
|
1499
1749
|
}
|
|
1500
|
-
var STYLE = `
|
|
1501
|
-
:root{color-scheme:dark;--bg:#0f1115;--panel:#171a21;--fg:#e6e8eb;--muted:#8b929c;--good:#3fb950;--local:#58a6ff;--frontier:#d29922;--line:#262b34}
|
|
1502
|
-
*{box-sizing:border-box}
|
|
1503
|
-
body{margin:0;background:var(--bg);color:var(--fg);font:14px/1.5 ui-sans-serif,system-ui,Segoe UI,Roboto,sans-serif}
|
|
1504
|
-
header{padding:16px 24px;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;align-items:baseline;gap:12px;flex-wrap:wrap}
|
|
1505
|
-
header h1{font-size:18px;margin:0}
|
|
1506
|
-
header .meta{color:var(--muted);font-size:12px}
|
|
1507
|
-
main{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:16px;padding:24px}
|
|
1508
|
-
.panel{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:16px}
|
|
1509
|
-
.panel h2{font-size:14px;margin:0 0 12px;color:var(--muted);text-transform:uppercase;letter-spacing:.04em}
|
|
1510
|
-
.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px}
|
|
1511
|
-
.stat{display:flex;flex-direction:column;gap:2px}
|
|
1512
|
-
.stat .k{color:var(--muted);font-size:12px}
|
|
1513
|
-
.stat .v{font-size:18px;font-weight:600}
|
|
1514
|
-
.good{color:var(--good)}
|
|
1515
|
-
.local{color:var(--local)}
|
|
1516
|
-
.frontier{color:var(--frontier)}
|
|
1517
|
-
.muted{color:var(--muted)}
|
|
1518
|
-
table{width:100%;border-collapse:collapse;font-size:13px}
|
|
1519
|
-
th,td{text-align:left;padding:6px 8px;border-bottom:1px solid var(--line)}
|
|
1520
|
-
th{color:var(--muted);font-weight:600}
|
|
1521
|
-
.hash{font-family:ui-monospace,Consolas,monospace;color:var(--muted)}
|
|
1522
|
-
.progress{height:10px;background:#0b0d11;border-radius:6px;overflow:hidden;border:1px solid var(--line)}
|
|
1523
|
-
.progress .bar{height:100%;background:var(--good)}
|
|
1524
|
-
ul.items,ul.timeline{list-style:none;margin:8px 0 0;padding:0;max-height:320px;overflow:auto}
|
|
1525
|
-
ul.items li,ul.timeline li{padding:4px 0;border-bottom:1px solid var(--line)}
|
|
1526
|
-
ul.items li.done{color:var(--muted)}
|
|
1527
|
-
ul.items .box{font-family:monospace}
|
|
1528
|
-
.who{color:var(--local)}
|
|
1529
|
-
.tag{color:var(--frontier);font-size:12px}
|
|
1530
|
-
.kind{display:inline-block;min-width:66px;color:var(--muted);font-size:12px}
|
|
1531
|
-
code{font-family:ui-monospace,Consolas,monospace;background:#0b0d11;padding:1px 5px;border-radius:4px}
|
|
1532
|
-
.mono{font-family:ui-monospace,Consolas,monospace;font-size:12px}
|
|
1533
|
-
.tabs{display:flex;gap:6px;padding:12px 24px 0;flex-wrap:wrap;border-bottom:1px solid var(--line)}
|
|
1534
|
-
.tabs .tab{background:var(--panel);color:var(--fg);border:1px solid var(--line);border-bottom:none;border-radius:8px 8px 0 0;padding:8px 14px;cursor:pointer;font:inherit;display:flex;align-items:center;gap:6px}
|
|
1535
|
-
.tabs .tab.active{background:var(--bg);border-color:var(--local);color:var(--fg)}
|
|
1536
|
-
.dot{width:8px;height:8px;border-radius:50%;display:inline-block;background:var(--muted)}
|
|
1537
|
-
.badge{font-size:11px;padding:1px 8px;border-radius:10px;border:1px solid var(--line);color:var(--muted);vertical-align:middle}
|
|
1538
|
-
.status-running{background:var(--good)}
|
|
1539
|
-
.badge.status-running{color:var(--good);border-color:var(--good)}
|
|
1540
|
-
.status-idle{background:var(--muted)}
|
|
1541
|
-
.badge.status-idle{color:var(--muted)}
|
|
1542
|
-
.status-waiting-input{background:var(--frontier)}
|
|
1543
|
-
.badge.status-waiting-input{color:var(--frontier);border-color:var(--frontier)}
|
|
1544
|
-
.status-ended{background:#6e7681}
|
|
1545
|
-
.badge.status-ended{color:#6e7681}
|
|
1546
|
-
.term .termlog{background:#0b0d11;border:1px solid var(--line);border-radius:8px;padding:10px;max-height:340px;overflow:auto;font-family:ui-monospace,Consolas,monospace;font-size:12px;white-space:pre-wrap}
|
|
1547
|
-
.term .tl{padding:1px 0}
|
|
1548
|
-
.term .tl.input{color:var(--local)}
|
|
1549
|
-
.term .tl.tool{color:var(--frontier)}
|
|
1550
|
-
.term .tl.status{color:var(--muted)}
|
|
1551
|
-
.term .tl.error{color:#f85149}
|
|
1552
|
-
.term .tl.permission{color:var(--frontier);font-weight:600}
|
|
1553
|
-
.term form.terminput{display:flex;gap:8px;margin-top:10px}
|
|
1554
|
-
.term form.terminput input{flex:1;background:#0b0d11;color:var(--fg);border:1px solid var(--line);border-radius:8px;padding:8px 10px;font:inherit}
|
|
1555
|
-
.term form.terminput button{background:var(--local);color:#04122b;border:none;border-radius:8px;padding:8px 16px;cursor:pointer;font:inherit;font-weight:600}
|
|
1556
|
-
.pty .ptylog{background:#05070a;border:1px solid var(--line);border-radius:8px;padding:10px;max-height:340px;overflow:auto;font-family:ui-monospace,Consolas,monospace;font-size:12px;white-space:pre-wrap;color:#c8d1dc}
|
|
1557
|
-
.pty form.ptyinput{display:flex;gap:8px;margin-top:10px}
|
|
1558
|
-
.pty form.ptyinput input{flex:1;background:#05070a;color:var(--fg);border:1px solid var(--line);border-radius:8px;padding:8px 10px;font:inherit}
|
|
1559
|
-
.pty form.ptyinput button,.pty .pty-start{background:var(--frontier);color:#1a1200;border:none;border-radius:8px;padding:8px 16px;cursor:pointer;font:inherit;font-weight:600;margin-top:8px}
|
|
1560
|
-
.pty .pty-start:disabled{opacity:.5;cursor:default}
|
|
1561
|
-
`;
|
|
1562
|
-
var CLIENT_JS = `
|
|
1563
|
-
(function(){
|
|
1564
|
-
function reloadIfChanged(prev){
|
|
1565
|
-
fetch('/api/state').then(function(r){return r.json()}).then(function(s){
|
|
1566
|
-
if(s.generatedAt!==prev){location.reload()}
|
|
1567
|
-
}).catch(function(){});
|
|
1568
|
-
}
|
|
1569
|
-
var current=document.documentElement.getAttribute('data-generated')||'';
|
|
1570
|
-
if('EventSource' in window){
|
|
1571
|
-
try{
|
|
1572
|
-
var es=new EventSource('/events');
|
|
1573
|
-
es.addEventListener('state',function(){location.reload()});
|
|
1574
|
-
es.onerror=function(){/* fallback below */};
|
|
1575
|
-
}catch(e){/* ignore */}
|
|
1576
|
-
}
|
|
1577
|
-
var ms=parseInt(document.documentElement.getAttribute('data-refresh')||'2000',10);
|
|
1578
|
-
setInterval(function(){reloadIfChanged(current)},isNaN(ms)?2000:Math.max(250,ms));
|
|
1579
|
-
|
|
1580
|
-
function selectTab(name){
|
|
1581
|
-
var tabs=document.querySelectorAll('#tabbar .tab');
|
|
1582
|
-
for(var i=0;i<tabs.length;i++){tabs[i].classList.toggle('active',tabs[i].getAttribute('data-tab')===name)}
|
|
1583
|
-
var views=document.querySelectorAll('main .view');
|
|
1584
|
-
var matched=false;
|
|
1585
|
-
for(var j=0;j<views.length;j++){
|
|
1586
|
-
var show=views[j].getAttribute('data-view')===name;
|
|
1587
|
-
views[j].hidden=!show;
|
|
1588
|
-
if(show){matched=true;if(name!=='all'){connectTerminal(views[j],name)}}
|
|
1589
|
-
}
|
|
1590
|
-
if(!matched){return false}
|
|
1591
|
-
try{sessionStorage.setItem('openteam.tab',name)}catch(e){}
|
|
1592
|
-
return true;
|
|
1593
|
-
}
|
|
1594
|
-
function connectTerminal(view,id){
|
|
1595
|
-
if(view.__wired){return}
|
|
1596
|
-
var root=document.documentElement;
|
|
1597
|
-
var token=root.getAttribute('data-token');
|
|
1598
|
-
if(root.getAttribute('data-terminal')!=='1'||!token){return}
|
|
1599
|
-
var log=view.querySelector('.termlog');
|
|
1600
|
-
if(!log){return}
|
|
1601
|
-
view.__wired=true;
|
|
1602
|
-
function append(cls,text){
|
|
1603
|
-
var line=document.createElement('div');
|
|
1604
|
-
line.className='tl '+cls;line.textContent=text;
|
|
1605
|
-
log.appendChild(line);log.scrollTop=log.scrollHeight;
|
|
1606
|
-
}
|
|
1607
|
-
var q='/console/session/'+encodeURIComponent(id);
|
|
1608
|
-
var tk='token='+encodeURIComponent(token);
|
|
1609
|
-
if('EventSource' in window){
|
|
1610
|
-
try{
|
|
1611
|
-
var es=new EventSource(q+'/stream?'+tk);
|
|
1612
|
-
es.addEventListener('frame',function(e){try{var f=JSON.parse(e.data);append(f.kind,f.text)}catch(_){}});
|
|
1613
|
-
es.onerror=function(){/* reconecta solo */};
|
|
1614
|
-
}catch(_){/* ignore */}
|
|
1615
|
-
}
|
|
1616
|
-
var form=view.querySelector('form.terminput');
|
|
1617
|
-
if(form){
|
|
1618
|
-
form.addEventListener('submit',function(e){
|
|
1619
|
-
e.preventDefault();
|
|
1620
|
-
var inp=form.querySelector('input[name=text]');
|
|
1621
|
-
var text=inp.value;
|
|
1622
|
-
if(!text){return}
|
|
1623
|
-
inp.value='';append('input','› '+text);
|
|
1624
|
-
fetch(q+'/input?'+tk,{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({text:text})})
|
|
1625
|
-
.then(function(r){if(!r.ok){append('error','✖ envío rechazado ('+r.status+')')}})
|
|
1626
|
-
.catch(function(){append('error','✖ no se pudo enviar')});
|
|
1627
|
-
});
|
|
1628
|
-
}
|
|
1629
|
-
connectPty(view,id,q,tk);
|
|
1630
|
-
}
|
|
1631
|
-
function connectPty(view,id,q,tk){
|
|
1632
|
-
var root=document.documentElement;
|
|
1633
|
-
if(root.getAttribute('data-pty')!=='1'){return}
|
|
1634
|
-
var pane=view.querySelector('.pty');
|
|
1635
|
-
if(!pane){return}
|
|
1636
|
-
var log=pane.querySelector('.ptylog');
|
|
1637
|
-
var startBtn=pane.querySelector('.pty-start');
|
|
1638
|
-
var input=pane.querySelector('form.ptyinput');
|
|
1639
|
-
function plog(text){
|
|
1640
|
-
var d=document.createElement('span');d.textContent=text;
|
|
1641
|
-
log.appendChild(d);log.scrollTop=log.scrollHeight;
|
|
1642
|
-
}
|
|
1643
|
-
if(!startBtn){return}
|
|
1644
|
-
startBtn.addEventListener('click',function(){
|
|
1645
|
-
startBtn.disabled=true;plog('Abriendo shell…
|
|
1646
|
-
');
|
|
1647
|
-
fetch(q+'/pty?'+tk,{method:'POST',headers:{'content-type':'application/json'},body:'{}'})
|
|
1648
|
-
.then(function(r){return r.ok?r.json():Promise.reject(r.status)})
|
|
1649
|
-
.then(function(info){openPtyWs(info.wsUrl)})
|
|
1650
|
-
.catch(function(s){plog('✖ no se pudo abrir PTY ('+s+')
|
|
1651
|
-
');startBtn.disabled=false});
|
|
1652
|
-
});
|
|
1653
|
-
function openPtyWs(url){
|
|
1654
|
-
try{
|
|
1655
|
-
var ws=new WebSocket(url);
|
|
1656
|
-
ws.binaryType='arraybuffer';
|
|
1657
|
-
ws.onmessage=function(e){
|
|
1658
|
-
var data=e.data;
|
|
1659
|
-
if(data instanceof ArrayBuffer){data=new TextDecoder().decode(new Uint8Array(data))}
|
|
1660
|
-
plog(data);
|
|
1661
|
-
};
|
|
1662
|
-
ws.onclose=function(){plog('
|
|
1663
|
-
[shell cerrada]
|
|
1664
|
-
');startBtn.disabled=false};
|
|
1665
|
-
ws.onerror=function(){plog('
|
|
1666
|
-
✖ error de WebSocket
|
|
1667
|
-
')};
|
|
1668
|
-
if(input){
|
|
1669
|
-
input.hidden=false;
|
|
1670
|
-
input.addEventListener('submit',function(e){
|
|
1671
|
-
e.preventDefault();
|
|
1672
|
-
var c=input.querySelector('input[name=cmd]');
|
|
1673
|
-
if(!c.value){return}
|
|
1674
|
-
ws.send(c.value+'\r');c.value='';
|
|
1675
|
-
});
|
|
1676
|
-
}
|
|
1677
|
-
}catch(_){plog('
|
|
1678
|
-
✖ WebSocket no disponible
|
|
1679
|
-
');startBtn.disabled=false}
|
|
1680
|
-
}
|
|
1681
|
-
}
|
|
1682
|
-
var bar=document.getElementById('tabbar');
|
|
1683
|
-
if(bar){
|
|
1684
|
-
bar.addEventListener('click',function(e){
|
|
1685
|
-
var t=e.target;
|
|
1686
|
-
while(t&&t!==bar&&!t.getAttribute('data-tab')){t=t.parentNode}
|
|
1687
|
-
if(t&&t.getAttribute('data-tab')){selectTab(t.getAttribute('data-tab'))}
|
|
1688
|
-
});
|
|
1689
|
-
}
|
|
1690
|
-
var saved='all';
|
|
1691
|
-
try{saved=sessionStorage.getItem('openteam.tab')||'all'}catch(e){}
|
|
1692
|
-
if(!selectTab(saved)){selectTab('all')}
|
|
1693
|
-
})();
|
|
1694
|
-
`;
|
|
1695
1750
|
function renderConsoleHtml(state, options = {}) {
|
|
1696
1751
|
const refreshMs = options.refreshMs ?? 2000;
|
|
1697
1752
|
const terminalEnabled = options.terminalEnabled === true;
|
|
@@ -1720,7 +1775,7 @@ function renderConsoleHtml(state, options = {}) {
|
|
|
1720
1775
|
'<meta charset="utf-8">',
|
|
1721
1776
|
'<meta name="viewport" content="width=device-width,initial-scale=1">',
|
|
1722
1777
|
"<title>openteam console</title>",
|
|
1723
|
-
`<style>${
|
|
1778
|
+
`<style>${CONSOLE_STYLE}</style>`,
|
|
1724
1779
|
"</head>",
|
|
1725
1780
|
"<body>",
|
|
1726
1781
|
"<header>",
|
|
@@ -1729,7 +1784,7 @@ function renderConsoleHtml(state, options = {}) {
|
|
|
1729
1784
|
"</header>",
|
|
1730
1785
|
tabBar(state.sessionsDetail),
|
|
1731
1786
|
`<main>${body}</main>`,
|
|
1732
|
-
`<script>${
|
|
1787
|
+
`<script>${CONSOLE_CLIENT_JS}</script>`,
|
|
1733
1788
|
"</body>",
|
|
1734
1789
|
"</html>"
|
|
1735
1790
|
].join("");
|
|
@@ -2359,6 +2414,18 @@ var HTML_HEADERS = { "content-type": "text/html; charset=utf-8" };
|
|
|
2359
2414
|
function isAddressInUse(error) {
|
|
2360
2415
|
return typeof error === "object" && error !== null && "code" in error && error.code === "EADDRINUSE";
|
|
2361
2416
|
}
|
|
2417
|
+
function bearerFromHeader3(request) {
|
|
2418
|
+
const header = request.headers.authorization;
|
|
2419
|
+
if (typeof header !== "string" || !header.startsWith("Bearer ")) {
|
|
2420
|
+
return;
|
|
2421
|
+
}
|
|
2422
|
+
const value = header.slice("Bearer ".length).trim();
|
|
2423
|
+
return value.length === 0 ? undefined : value;
|
|
2424
|
+
}
|
|
2425
|
+
function readAuthorized(request, url, token) {
|
|
2426
|
+
const provided = tokenFromQuery(url.searchParams) ?? bearerFromHeader3(request);
|
|
2427
|
+
return provided !== undefined && safeEqualToken(provided, token);
|
|
2428
|
+
}
|
|
2362
2429
|
async function renderState(deps) {
|
|
2363
2430
|
return renderConsoleHtml(buildConsoleState(await deps.readSnapshot()), {
|
|
2364
2431
|
refreshMs: deps.config.refreshMs,
|
|
@@ -2426,6 +2493,11 @@ async function createConsoleServer(deps) {
|
|
|
2426
2493
|
return;
|
|
2427
2494
|
}
|
|
2428
2495
|
const path = url2.pathname;
|
|
2496
|
+
const remoteToken = deps.storageApi?.token;
|
|
2497
|
+
if (remoteToken !== undefined && (path === "/" || path === "/index.html" || path === "/api/state" || path === "/events") && !readAuthorized(request, url2, remoteToken)) {
|
|
2498
|
+
response.writeHead(401, JSON_HEADERS3).end(JSON.stringify({ error: "unauthorized" }));
|
|
2499
|
+
return;
|
|
2500
|
+
}
|
|
2429
2501
|
if (path === "/" || path === "/index.html") {
|
|
2430
2502
|
response.writeHead(200, HTML_HEADERS).end(await renderState(deps));
|
|
2431
2503
|
return;
|
|
@@ -2465,7 +2537,12 @@ async function createConsoleServer(deps) {
|
|
|
2465
2537
|
break;
|
|
2466
2538
|
}
|
|
2467
2539
|
if (await tryListen(server, deps.config.host, port)) {
|
|
2468
|
-
|
|
2540
|
+
const address = server.address();
|
|
2541
|
+
if (typeof address !== "object" || address === null) {
|
|
2542
|
+
server.close();
|
|
2543
|
+
throw new Error("console: bound address is unavailable");
|
|
2544
|
+
}
|
|
2545
|
+
boundPort = address.port;
|
|
2469
2546
|
bound = true;
|
|
2470
2547
|
break;
|
|
2471
2548
|
}
|
|
@@ -2899,6 +2976,8 @@ async function runConsoleServe(deps, options) {
|
|
|
2899
2976
|
deps.log("Escuchando en loopback. Pulsa Ctrl+C para parar.");
|
|
2900
2977
|
if (config.console.remoteStorage) {
|
|
2901
2978
|
deps.log("Storage remoto expuesto en /storage/* (read-only, requiere token).");
|
|
2979
|
+
deps.log("La consola y el estado exigen token; ábrela con la URL firmada:");
|
|
2980
|
+
deps.log(` ${runtime.url}/?token=${token}`);
|
|
2902
2981
|
}
|
|
2903
2982
|
if (config.storage.sqliteIndex.enabled) {
|
|
2904
2983
|
const buildIndex = deps.buildSqliteIndex ?? buildSqliteIndexFromConfig;
|
|
@@ -3220,6 +3299,9 @@ function normalizeBaseURL(baseURL) {
|
|
|
3220
3299
|
function modelsEndpoint(baseURL) {
|
|
3221
3300
|
return `${normalizeBaseURL(baseURL)}/models`;
|
|
3222
3301
|
}
|
|
3302
|
+
function embeddingsEndpoint(baseURL) {
|
|
3303
|
+
return `${normalizeBaseURL(baseURL)}/embeddings`;
|
|
3304
|
+
}
|
|
3223
3305
|
function errorMessage(error) {
|
|
3224
3306
|
if (error instanceof Error) {
|
|
3225
3307
|
return error.message;
|
|
@@ -4031,7 +4113,7 @@ function buildOrchestratorAgent(frontier, options = {}) {
|
|
|
4031
4113
|
];
|
|
4032
4114
|
const frontmatter = [
|
|
4033
4115
|
"---",
|
|
4034
|
-
'description: "Orquestador
|
|
4116
|
+
'description: "Orquestador multiagente de openteam: en cada petición comprueba si existe equipo; si no, entiende las tareas y crea el equipo de subagentes a demanda, planifica dependencias, paraleliza el trabajo independiente y mantiene el todo con el responsable de cada tarea, con routing local-first."',
|
|
4035
4117
|
"mode: primary",
|
|
4036
4118
|
`model: ${model}`,
|
|
4037
4119
|
"temperature: 0.2",
|
|
@@ -4040,7 +4122,7 @@ function buildOrchestratorAgent(frontier, options = {}) {
|
|
|
4040
4122
|
].join(`
|
|
4041
4123
|
`);
|
|
4042
4124
|
const body = [
|
|
4043
|
-
"Eres el orquestador de **openteam
|
|
4125
|
+
"Eres el orquestador multiagente de **openteam**. Eres el único agente",
|
|
4044
4126
|
"primario: no existe un equipo precreado. Tú **construyes el equipo de",
|
|
4045
4127
|
"subagentes a demanda** según el objetivo del usuario y del repositorio.",
|
|
4046
4128
|
"",
|
|
@@ -4066,7 +4148,7 @@ function buildOrchestratorAgent(frontier, options = {}) {
|
|
|
4066
4148
|
"",
|
|
4067
4149
|
"## Casting: elige un universo y nombra al equipo",
|
|
4068
4150
|
"",
|
|
4069
|
-
"
|
|
4151
|
+
"El equipo tiene una identidad temática persistente:",
|
|
4070
4152
|
"",
|
|
4071
4153
|
"1. Al crear el equipo por primera vez, **elige un universo** temático (p. ej.",
|
|
4072
4154
|
" una película, serie, cómic o mitología). Si el usuario tiene preferencia,",
|
|
@@ -4251,9 +4333,9 @@ var HELP = [
|
|
|
4251
4333
|
" openteam local on Reactiva el routing local-first",
|
|
4252
4334
|
" openteam doctor Diagnóstico de runtimes y config",
|
|
4253
4335
|
" openteam agents Lista los agentes y el LLM (local/frontier) de cada uno",
|
|
4254
|
-
" openteam console Lanza la Console web multi-sesión (Ctrl+C para parar; --open abre el navegador)",
|
|
4255
|
-
" openteam console --status Muestra la config de la Console sin lanzarla",
|
|
4256
|
-
" openteam tunnel Expone la Console vía Dev Tunnels (--terminal expone opencode; Ctrl+C para cerrar)",
|
|
4336
|
+
" openteam console [alfa] Lanza la Console web multi-sesión (Ctrl+C para parar; --open abre el navegador)",
|
|
4337
|
+
" openteam console --status [alfa] Muestra la config de la Console sin lanzarla",
|
|
4338
|
+
" openteam tunnel [alfa] Expone la Console vía Dev Tunnels (--terminal expone opencode; Ctrl+C para cerrar)",
|
|
4257
4339
|
" openteam mcp Servidor MCP de solo lectura (memoria del equipo) sobre stdio",
|
|
4258
4340
|
" openteam report Resumen de coste/ahorro (telemetría)",
|
|
4259
4341
|
" openteam yolo status Muestra si el modo YOLO está activo",
|
|
@@ -4497,6 +4579,32 @@ async function writeOpenTeamConfigFile(config, path = DEFAULT_CONFIG_PATH, stora
|
|
|
4497
4579
|
await storage.write(path, serializeOpenTeamConfig(config));
|
|
4498
4580
|
}
|
|
4499
4581
|
|
|
4582
|
+
// src/commands/frontierCatalog.ts
|
|
4583
|
+
function weightedCost(inputUSD, outputUSD) {
|
|
4584
|
+
return 0.75 * inputUSD + 0.25 * outputUSD;
|
|
4585
|
+
}
|
|
4586
|
+
function profileWeightedCost(profile) {
|
|
4587
|
+
return weightedCost(profile.costPer1M.inputUSD, profile.costPer1M.outputUSD);
|
|
4588
|
+
}
|
|
4589
|
+
function sortFrontierByCost(profiles) {
|
|
4590
|
+
return [...profiles].sort((a, b) => profileWeightedCost(a) - profileWeightedCost(b));
|
|
4591
|
+
}
|
|
4592
|
+
function isFreeFrontier(profile) {
|
|
4593
|
+
return profile.costPer1M.inputUSD === 0 && profile.costPer1M.outputUSD === 0;
|
|
4594
|
+
}
|
|
4595
|
+
function frontierCostLabel(profile) {
|
|
4596
|
+
if (isFreeFrontier(profile)) {
|
|
4597
|
+
return "gratis";
|
|
4598
|
+
}
|
|
4599
|
+
return `$${profile.costPer1M.inputUSD}/$${profile.costPer1M.outputUSD} por 1M`;
|
|
4600
|
+
}
|
|
4601
|
+
function frontierProviders(profiles) {
|
|
4602
|
+
return [...new Set(profiles.map((profile) => profile.ref.providerID))].sort((a, b) => a.localeCompare(b));
|
|
4603
|
+
}
|
|
4604
|
+
function frontierModelsForProvider(profiles, providerID) {
|
|
4605
|
+
return sortFrontierByCost(profiles.filter((profile) => profile.ref.providerID === providerID));
|
|
4606
|
+
}
|
|
4607
|
+
|
|
4500
4608
|
// src/commands/slashCommand.ts
|
|
4501
4609
|
var OPENTEAM_COMMAND_DIR = ".opencode/command";
|
|
4502
4610
|
var OPENTEAM_COMMAND_PATH = `${OPENTEAM_COMMAND_DIR}/openteam.md`;
|
|
@@ -4580,30 +4688,6 @@ var KNOWN_RUNTIMES = [
|
|
|
4580
4688
|
dynamicPort: true
|
|
4581
4689
|
}
|
|
4582
4690
|
];
|
|
4583
|
-
function weightedCost(inputUSD, outputUSD) {
|
|
4584
|
-
return 0.75 * inputUSD + 0.25 * outputUSD;
|
|
4585
|
-
}
|
|
4586
|
-
function profileWeightedCost(profile) {
|
|
4587
|
-
return weightedCost(profile.costPer1M.inputUSD, profile.costPer1M.outputUSD);
|
|
4588
|
-
}
|
|
4589
|
-
function sortFrontierByCost(profiles) {
|
|
4590
|
-
return [...profiles].sort((a, b) => profileWeightedCost(a) - profileWeightedCost(b));
|
|
4591
|
-
}
|
|
4592
|
-
function isFreeFrontier(profile) {
|
|
4593
|
-
return profile.costPer1M.inputUSD === 0 && profile.costPer1M.outputUSD === 0;
|
|
4594
|
-
}
|
|
4595
|
-
function frontierCostLabel(profile) {
|
|
4596
|
-
if (isFreeFrontier(profile)) {
|
|
4597
|
-
return "gratis";
|
|
4598
|
-
}
|
|
4599
|
-
return `$${profile.costPer1M.inputUSD}/$${profile.costPer1M.outputUSD} por 1M`;
|
|
4600
|
-
}
|
|
4601
|
-
function frontierProviders(profiles) {
|
|
4602
|
-
return [...new Set(profiles.map((profile) => profile.ref.providerID))].sort((a, b) => a.localeCompare(b));
|
|
4603
|
-
}
|
|
4604
|
-
function frontierModelsForProvider(profiles, providerID) {
|
|
4605
|
-
return sortFrontierByCost(profiles.filter((profile) => profile.ref.providerID === providerID));
|
|
4606
|
-
}
|
|
4607
4691
|
function knownRuntime(id) {
|
|
4608
4692
|
const found = KNOWN_RUNTIMES.find((runtime) => runtime.id === id);
|
|
4609
4693
|
if (found === undefined) {
|
|
@@ -5044,6 +5128,210 @@ function loadOpenTeamConfig(raw) {
|
|
|
5044
5128
|
return OpenTeamConfigSchema.parse(raw ?? {});
|
|
5045
5129
|
}
|
|
5046
5130
|
|
|
5131
|
+
// src/memory/types.ts
|
|
5132
|
+
import { z as z6 } from "zod";
|
|
5133
|
+
var SHARED_OWNER_KEY = "*";
|
|
5134
|
+
var OwnerKeySchema = z6.string().min(1);
|
|
5135
|
+
var MemoryKindSchema = z6.enum(["fact", "preference", "entity"]);
|
|
5136
|
+
var MemoryBaseSchema = z6.object({
|
|
5137
|
+
id: z6.string().min(1),
|
|
5138
|
+
ownerKey: OwnerKeySchema,
|
|
5139
|
+
confidence: z6.number().min(0).max(1),
|
|
5140
|
+
validFrom: z6.number().finite(),
|
|
5141
|
+
validUntil: z6.number().finite().nullable().default(null),
|
|
5142
|
+
createdAt: z6.number().finite(),
|
|
5143
|
+
invalidatedAt: z6.number().finite().nullable().default(null),
|
|
5144
|
+
sourceHash: z6.string().min(1),
|
|
5145
|
+
supersededBy: z6.string().min(1).nullable().default(null)
|
|
5146
|
+
});
|
|
5147
|
+
var FactSchema = MemoryBaseSchema.extend({
|
|
5148
|
+
kind: z6.literal("fact"),
|
|
5149
|
+
subject: z6.string().min(1),
|
|
5150
|
+
predicate: z6.string().min(1),
|
|
5151
|
+
object: z6.string().min(1),
|
|
5152
|
+
category: z6.string().min(1).nullable().default(null)
|
|
5153
|
+
});
|
|
5154
|
+
var PreferenceSchema = MemoryBaseSchema.extend({
|
|
5155
|
+
kind: z6.literal("preference"),
|
|
5156
|
+
category: z6.string().min(1),
|
|
5157
|
+
preference: z6.string().min(1),
|
|
5158
|
+
context: z6.string().min(1).nullable().default(null),
|
|
5159
|
+
lastAccessedAt: z6.number().finite().nullable().default(null),
|
|
5160
|
+
accessCount: z6.number().int().min(0).default(0)
|
|
5161
|
+
});
|
|
5162
|
+
var EntitySchema = MemoryBaseSchema.extend({
|
|
5163
|
+
kind: z6.literal("entity"),
|
|
5164
|
+
canonicalName: z6.string().min(1),
|
|
5165
|
+
type: z6.string().min(1),
|
|
5166
|
+
aliases: z6.array(z6.string().min(1)).default([])
|
|
5167
|
+
});
|
|
5168
|
+
var MemoryRecordSchema = z6.discriminatedUnion("kind", [
|
|
5169
|
+
FactSchema,
|
|
5170
|
+
PreferenceSchema,
|
|
5171
|
+
EntitySchema
|
|
5172
|
+
]);
|
|
5173
|
+
var RecallQuerySchema = z6.object({
|
|
5174
|
+
ownerKey: OwnerKeySchema,
|
|
5175
|
+
kinds: z6.array(MemoryKindSchema).default(["fact", "preference", "entity"]),
|
|
5176
|
+
asOf: z6.number().finite().nullable().default(null),
|
|
5177
|
+
limit: z6.number().int().positive().default(8),
|
|
5178
|
+
minSimilarity: z6.number().min(0).max(1).default(0.2),
|
|
5179
|
+
includeShared: z6.boolean().default(true)
|
|
5180
|
+
});
|
|
5181
|
+
|
|
5182
|
+
// src/memory/rank.ts
|
|
5183
|
+
var DAY_MS = 86400000;
|
|
5184
|
+
var DEFAULT_RANK_SIGNALS = {
|
|
5185
|
+
similarityWeight: 1,
|
|
5186
|
+
recencyWeight: 0.2,
|
|
5187
|
+
accessWeight: 0.1,
|
|
5188
|
+
recencyHalfLifeMs: 14 * DAY_MS
|
|
5189
|
+
};
|
|
5190
|
+
function recencyTimestamp(record) {
|
|
5191
|
+
if (record.kind === "preference" && record.lastAccessedAt !== null) {
|
|
5192
|
+
return record.lastAccessedAt;
|
|
5193
|
+
}
|
|
5194
|
+
return record.createdAt;
|
|
5195
|
+
}
|
|
5196
|
+
function recencyScore(record, signals) {
|
|
5197
|
+
const ageMs = Math.max(0, signals.now - recencyTimestamp(record));
|
|
5198
|
+
return 0.5 ** (ageMs / signals.recencyHalfLifeMs);
|
|
5199
|
+
}
|
|
5200
|
+
function accessScore(record) {
|
|
5201
|
+
if (record.kind !== "preference") {
|
|
5202
|
+
return 0;
|
|
5203
|
+
}
|
|
5204
|
+
return 1 - 1 / (1 + record.accessCount);
|
|
5205
|
+
}
|
|
5206
|
+
function scoreCandidate(candidate, signals) {
|
|
5207
|
+
return signals.similarityWeight * candidate.similarity + signals.recencyWeight * recencyScore(candidate.record, signals) + signals.accessWeight * accessScore(candidate.record);
|
|
5208
|
+
}
|
|
5209
|
+
function rankRecall(candidates, signals) {
|
|
5210
|
+
return candidates.map((candidate) => ({
|
|
5211
|
+
...candidate,
|
|
5212
|
+
score: scoreCandidate(candidate, signals)
|
|
5213
|
+
})).sort((a, b) => {
|
|
5214
|
+
if (b.score !== a.score) {
|
|
5215
|
+
return b.score - a.score;
|
|
5216
|
+
}
|
|
5217
|
+
return a.record.id < b.record.id ? -1 : a.record.id > b.record.id ? 1 : 0;
|
|
5218
|
+
});
|
|
5219
|
+
}
|
|
5220
|
+
|
|
5221
|
+
// src/memory/temporal.ts
|
|
5222
|
+
function isTransactionActiveAt(record, asOf) {
|
|
5223
|
+
return record.invalidatedAt === null || record.invalidatedAt > asOf;
|
|
5224
|
+
}
|
|
5225
|
+
function isValidAt(record, asOf) {
|
|
5226
|
+
if (record.validFrom > asOf) {
|
|
5227
|
+
return false;
|
|
5228
|
+
}
|
|
5229
|
+
return record.validUntil === null || record.validUntil > asOf;
|
|
5230
|
+
}
|
|
5231
|
+
function isActiveAt(record, asOf) {
|
|
5232
|
+
return isTransactionActiveAt(record, asOf) && isValidAt(record, asOf);
|
|
5233
|
+
}
|
|
5234
|
+
function filterActive(records, asOf) {
|
|
5235
|
+
return records.filter((record) => isActiveAt(record, asOf));
|
|
5236
|
+
}
|
|
5237
|
+
function coalesceWindow(a, b) {
|
|
5238
|
+
const validFrom = Math.min(a.validFrom, b.validFrom);
|
|
5239
|
+
const validUntil = a.validUntil === null || b.validUntil === null ? null : Math.max(a.validUntil, b.validUntil);
|
|
5240
|
+
return { validFrom, validUntil };
|
|
5241
|
+
}
|
|
5242
|
+
|
|
5243
|
+
// src/memory/vector.ts
|
|
5244
|
+
function dot(a, b) {
|
|
5245
|
+
if (a.length !== b.length) {
|
|
5246
|
+
throw new Error(`dimension mismatch: ${a.length} vs ${b.length}`);
|
|
5247
|
+
}
|
|
5248
|
+
let sum = 0;
|
|
5249
|
+
for (let i = 0;i < a.length; i += 1) {
|
|
5250
|
+
sum += a[i] * b[i];
|
|
5251
|
+
}
|
|
5252
|
+
return sum;
|
|
5253
|
+
}
|
|
5254
|
+
function norm(a) {
|
|
5255
|
+
let sum = 0;
|
|
5256
|
+
for (let i = 0;i < a.length; i += 1) {
|
|
5257
|
+
const value = a[i];
|
|
5258
|
+
sum += value * value;
|
|
5259
|
+
}
|
|
5260
|
+
return Math.sqrt(sum);
|
|
5261
|
+
}
|
|
5262
|
+
function cosineSimilarity(a, b) {
|
|
5263
|
+
const denom = norm(a) * norm(b);
|
|
5264
|
+
if (denom === 0) {
|
|
5265
|
+
return 0;
|
|
5266
|
+
}
|
|
5267
|
+
return dot(a, b) / denom;
|
|
5268
|
+
}
|
|
5269
|
+
|
|
5270
|
+
// src/memory/recall.ts
|
|
5271
|
+
function matchesScope(record, query) {
|
|
5272
|
+
if (record.ownerKey === query.ownerKey) {
|
|
5273
|
+
return true;
|
|
5274
|
+
}
|
|
5275
|
+
return query.includeShared && record.ownerKey === SHARED_OWNER_KEY;
|
|
5276
|
+
}
|
|
5277
|
+
function recall(records, queryEmbedding, query, deps) {
|
|
5278
|
+
const asOf = query.asOf ?? deps.now;
|
|
5279
|
+
const kinds = new Set(query.kinds);
|
|
5280
|
+
const candidates = [];
|
|
5281
|
+
for (const record of filterActive(records, asOf)) {
|
|
5282
|
+
if (!kinds.has(record.kind) || !matchesScope(record, query)) {
|
|
5283
|
+
continue;
|
|
5284
|
+
}
|
|
5285
|
+
const embedding = deps.embeddings.get(record.id);
|
|
5286
|
+
if (embedding === undefined) {
|
|
5287
|
+
continue;
|
|
5288
|
+
}
|
|
5289
|
+
const similarity = cosineSimilarity(queryEmbedding, embedding);
|
|
5290
|
+
if (similarity < query.minSimilarity) {
|
|
5291
|
+
continue;
|
|
5292
|
+
}
|
|
5293
|
+
candidates.push({ record, similarity });
|
|
5294
|
+
}
|
|
5295
|
+
const signals = {
|
|
5296
|
+
now: deps.now,
|
|
5297
|
+
...deps.signals ?? DEFAULT_RANK_SIGNALS
|
|
5298
|
+
};
|
|
5299
|
+
return rankRecall(candidates, signals).slice(0, query.limit);
|
|
5300
|
+
}
|
|
5301
|
+
|
|
5302
|
+
// src/storage/index/memoryRecall.ts
|
|
5303
|
+
async function recallMemory(deps, queryText, query) {
|
|
5304
|
+
const [queryEmbedding] = await deps.embedder([queryText]);
|
|
5305
|
+
if (queryEmbedding === undefined) {
|
|
5306
|
+
return [];
|
|
5307
|
+
}
|
|
5308
|
+
const records = deps.index.allRecords();
|
|
5309
|
+
const embeddings = deps.index.embeddingMap();
|
|
5310
|
+
const recallDeps = deps.signals === undefined ? { embeddings, now: deps.now } : { embeddings, now: deps.now, signals: deps.signals };
|
|
5311
|
+
return recall(records, queryEmbedding, query, recallDeps);
|
|
5312
|
+
}
|
|
5313
|
+
|
|
5314
|
+
// src/mcp/semanticRecall.ts
|
|
5315
|
+
function buildSemanticRecall(deps, policy) {
|
|
5316
|
+
return async ({ queryText, kinds, limit }) => {
|
|
5317
|
+
const query = RecallQuerySchema.parse({
|
|
5318
|
+
ownerKey: policy.ownerKey,
|
|
5319
|
+
kinds,
|
|
5320
|
+
asOf: policy.asOf ?? null,
|
|
5321
|
+
limit,
|
|
5322
|
+
minSimilarity: policy.minSimilarity,
|
|
5323
|
+
includeShared: policy.includeShared
|
|
5324
|
+
});
|
|
5325
|
+
const recallDeps = deps.signals === undefined ? { index: deps.index, embedder: deps.embedder, now: deps.now() } : {
|
|
5326
|
+
index: deps.index,
|
|
5327
|
+
embedder: deps.embedder,
|
|
5328
|
+
now: deps.now(),
|
|
5329
|
+
signals: deps.signals
|
|
5330
|
+
};
|
|
5331
|
+
return recallMemory(recallDeps, queryText, query);
|
|
5332
|
+
};
|
|
5333
|
+
}
|
|
5334
|
+
|
|
5047
5335
|
// src/mcp/teamMemory.ts
|
|
5048
5336
|
function buildTeamMemory(events) {
|
|
5049
5337
|
const aggregate = aggregateEvents(events);
|
|
@@ -5133,6 +5421,45 @@ var MCP_TOOLS = [
|
|
|
5133
5421
|
inputSchema: { type: "object", properties: {} }
|
|
5134
5422
|
}
|
|
5135
5423
|
];
|
|
5424
|
+
var RECALL_QUERY_PROP = {
|
|
5425
|
+
query: {
|
|
5426
|
+
type: "string",
|
|
5427
|
+
description: "Texto de consulta para el recall semántico."
|
|
5428
|
+
},
|
|
5429
|
+
...LIMIT_PROP
|
|
5430
|
+
};
|
|
5431
|
+
var MEMORY_TOOLS = [
|
|
5432
|
+
{
|
|
5433
|
+
name: "recall_facts",
|
|
5434
|
+
description: "Hechos relevantes (sujeto-predicado-objeto) para la consulta, por similitud semántica. Contenido redactado salvo storeContent.",
|
|
5435
|
+
inputSchema: {
|
|
5436
|
+
type: "object",
|
|
5437
|
+
properties: { ...RECALL_QUERY_PROP },
|
|
5438
|
+
required: ["query"]
|
|
5439
|
+
}
|
|
5440
|
+
},
|
|
5441
|
+
{
|
|
5442
|
+
name: "recall_preferences",
|
|
5443
|
+
description: "Preferencias del usuario/proyecto relevantes para la consulta, por similitud semántica.",
|
|
5444
|
+
inputSchema: {
|
|
5445
|
+
type: "object",
|
|
5446
|
+
properties: { ...RECALL_QUERY_PROP },
|
|
5447
|
+
required: ["query"]
|
|
5448
|
+
}
|
|
5449
|
+
},
|
|
5450
|
+
{
|
|
5451
|
+
name: "similar_tasks",
|
|
5452
|
+
description: "Memoria (hechos/preferencias/entidades) relevante a una tarea, por similitud semántica; útil para reutilizar contexto previo.",
|
|
5453
|
+
inputSchema: {
|
|
5454
|
+
type: "object",
|
|
5455
|
+
properties: { ...RECALL_QUERY_PROP },
|
|
5456
|
+
required: ["query"]
|
|
5457
|
+
}
|
|
5458
|
+
}
|
|
5459
|
+
];
|
|
5460
|
+
function availableTools(deps) {
|
|
5461
|
+
return deps.recallSemantic === undefined ? MCP_TOOLS : [...MCP_TOOLS, ...MEMORY_TOOLS];
|
|
5462
|
+
}
|
|
5136
5463
|
function response(id, result) {
|
|
5137
5464
|
return { jsonrpc: "2.0", id, result };
|
|
5138
5465
|
}
|
|
@@ -5148,7 +5475,58 @@ function optionalString(value) {
|
|
|
5148
5475
|
function optionalLimit(value) {
|
|
5149
5476
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
5150
5477
|
}
|
|
5478
|
+
var DEFAULT_RECALL_LIMIT = 8;
|
|
5479
|
+
function projectRecall(results) {
|
|
5480
|
+
return results.map(({ record, similarity, score }) => {
|
|
5481
|
+
const base = {
|
|
5482
|
+
id: record.id,
|
|
5483
|
+
kind: record.kind,
|
|
5484
|
+
ownerKey: record.ownerKey,
|
|
5485
|
+
confidence: record.confidence,
|
|
5486
|
+
similarity,
|
|
5487
|
+
score
|
|
5488
|
+
};
|
|
5489
|
+
if (record.kind === "fact") {
|
|
5490
|
+
return {
|
|
5491
|
+
...base,
|
|
5492
|
+
subject: record.subject,
|
|
5493
|
+
predicate: record.predicate,
|
|
5494
|
+
object: record.object,
|
|
5495
|
+
category: record.category
|
|
5496
|
+
};
|
|
5497
|
+
}
|
|
5498
|
+
if (record.kind === "preference") {
|
|
5499
|
+
return {
|
|
5500
|
+
...base,
|
|
5501
|
+
category: record.category,
|
|
5502
|
+
preference: record.preference
|
|
5503
|
+
};
|
|
5504
|
+
}
|
|
5505
|
+
return { ...base, canonicalName: record.canonicalName, type: record.type };
|
|
5506
|
+
});
|
|
5507
|
+
}
|
|
5508
|
+
async function callSemanticRecall(recall2, kinds, args) {
|
|
5509
|
+
const query = optionalString(args.query);
|
|
5510
|
+
if (query === undefined) {
|
|
5511
|
+
return textContent({ error: "requiere 'query'" });
|
|
5512
|
+
}
|
|
5513
|
+
const limit = optionalLimit(args.limit) ?? DEFAULT_RECALL_LIMIT;
|
|
5514
|
+
const results = await recall2({ queryText: query, kinds, limit });
|
|
5515
|
+
return textContent(projectRecall(results));
|
|
5516
|
+
}
|
|
5151
5517
|
async function callTool(name, args, deps) {
|
|
5518
|
+
if (deps.recallSemantic !== undefined) {
|
|
5519
|
+
switch (name) {
|
|
5520
|
+
case "recall_facts":
|
|
5521
|
+
return callSemanticRecall(deps.recallSemantic, ["fact"], args);
|
|
5522
|
+
case "recall_preferences":
|
|
5523
|
+
return callSemanticRecall(deps.recallSemantic, ["preference"], args);
|
|
5524
|
+
case "similar_tasks":
|
|
5525
|
+
return callSemanticRecall(deps.recallSemantic, ["fact", "preference", "entity"], args);
|
|
5526
|
+
default:
|
|
5527
|
+
break;
|
|
5528
|
+
}
|
|
5529
|
+
}
|
|
5152
5530
|
const memory = await deps.readMemory();
|
|
5153
5531
|
switch (name) {
|
|
5154
5532
|
case "list_decisions": {
|
|
@@ -5189,7 +5567,7 @@ async function handleMcpMessage(message, deps) {
|
|
|
5189
5567
|
serverInfo
|
|
5190
5568
|
});
|
|
5191
5569
|
case "tools/list":
|
|
5192
|
-
return response(id, { tools:
|
|
5570
|
+
return response(id, { tools: availableTools(deps) });
|
|
5193
5571
|
case "tools/call": {
|
|
5194
5572
|
const params = message.params ?? {};
|
|
5195
5573
|
const name = optionalString(params.name);
|
|
@@ -5258,6 +5636,456 @@ async function* linesFromReadable(readable) {
|
|
|
5258
5636
|
}
|
|
5259
5637
|
}
|
|
5260
5638
|
|
|
5639
|
+
// src/memory/consolidate.ts
|
|
5640
|
+
var DAY_MS2 = 86400000;
|
|
5641
|
+
var DEFAULT_DECAY_PARAMS = {
|
|
5642
|
+
halfLifeMs: 90 * DAY_MS2,
|
|
5643
|
+
forgetThreshold: 0.05
|
|
5644
|
+
};
|
|
5645
|
+
function lastReinforcedAt(record) {
|
|
5646
|
+
if (record.kind === "preference" && record.lastAccessedAt !== null) {
|
|
5647
|
+
return record.lastAccessedAt;
|
|
5648
|
+
}
|
|
5649
|
+
return record.createdAt;
|
|
5650
|
+
}
|
|
5651
|
+
function decayedConfidence(record, now, params = DEFAULT_DECAY_PARAMS) {
|
|
5652
|
+
const ageMs = Math.max(0, now - lastReinforcedAt(record));
|
|
5653
|
+
return record.confidence * 0.5 ** (ageMs / params.halfLifeMs);
|
|
5654
|
+
}
|
|
5655
|
+
function consolidate(records, now, params = DEFAULT_DECAY_PARAMS) {
|
|
5656
|
+
const tombstones = [];
|
|
5657
|
+
for (const record of records) {
|
|
5658
|
+
if (!isTransactionActiveAt(record, now)) {
|
|
5659
|
+
continue;
|
|
5660
|
+
}
|
|
5661
|
+
if (decayedConfidence(record, now, params) < params.forgetThreshold) {
|
|
5662
|
+
tombstones.push({ ...record, invalidatedAt: now, supersededBy: null });
|
|
5663
|
+
}
|
|
5664
|
+
}
|
|
5665
|
+
return tombstones;
|
|
5666
|
+
}
|
|
5667
|
+
|
|
5668
|
+
// src/memory/merge.ts
|
|
5669
|
+
var SEP = "\x00";
|
|
5670
|
+
function factSlotKey(fact) {
|
|
5671
|
+
return [fact.subject, fact.predicate, fact.ownerKey].join(SEP);
|
|
5672
|
+
}
|
|
5673
|
+
function factValueKey(fact) {
|
|
5674
|
+
return [factSlotKey(fact), fact.object].join(SEP);
|
|
5675
|
+
}
|
|
5676
|
+
function preferenceSlotKey(preference) {
|
|
5677
|
+
return [preference.category, preference.ownerKey].join(SEP);
|
|
5678
|
+
}
|
|
5679
|
+
function preferenceValueKey(preference) {
|
|
5680
|
+
return [preferenceSlotKey(preference), preference.preference].join(SEP);
|
|
5681
|
+
}
|
|
5682
|
+
function entitySlotKey(entity) {
|
|
5683
|
+
return [entity.canonicalName, entity.type, entity.ownerKey].join(SEP);
|
|
5684
|
+
}
|
|
5685
|
+
function coalesceFact(existing, incoming) {
|
|
5686
|
+
const window = coalesceWindow(existing, incoming);
|
|
5687
|
+
return {
|
|
5688
|
+
...existing,
|
|
5689
|
+
validFrom: window.validFrom,
|
|
5690
|
+
validUntil: window.validUntil,
|
|
5691
|
+
confidence: Math.max(existing.confidence, incoming.confidence)
|
|
5692
|
+
};
|
|
5693
|
+
}
|
|
5694
|
+
function applyFact(state, incoming, now) {
|
|
5695
|
+
const incomingSlot = factSlotKey(incoming);
|
|
5696
|
+
const incomingValue = factValueKey(incoming);
|
|
5697
|
+
const superseded = [];
|
|
5698
|
+
const records = [];
|
|
5699
|
+
let reaffirmed = false;
|
|
5700
|
+
for (const fact of state) {
|
|
5701
|
+
if (!isActiveAt(fact, now)) {
|
|
5702
|
+
records.push(fact);
|
|
5703
|
+
continue;
|
|
5704
|
+
}
|
|
5705
|
+
if (factValueKey(fact) === incomingValue) {
|
|
5706
|
+
records.push(coalesceFact(fact, incoming));
|
|
5707
|
+
reaffirmed = true;
|
|
5708
|
+
continue;
|
|
5709
|
+
}
|
|
5710
|
+
if (factSlotKey(fact) === incomingSlot) {
|
|
5711
|
+
const dead = {
|
|
5712
|
+
...fact,
|
|
5713
|
+
invalidatedAt: now,
|
|
5714
|
+
supersededBy: incoming.id
|
|
5715
|
+
};
|
|
5716
|
+
superseded.push(dead);
|
|
5717
|
+
records.push(dead);
|
|
5718
|
+
continue;
|
|
5719
|
+
}
|
|
5720
|
+
records.push(fact);
|
|
5721
|
+
}
|
|
5722
|
+
if (!reaffirmed) {
|
|
5723
|
+
records.push(incoming);
|
|
5724
|
+
}
|
|
5725
|
+
return { records, superseded };
|
|
5726
|
+
}
|
|
5727
|
+
function applyPreference(state, incoming, now) {
|
|
5728
|
+
const incomingSlot = preferenceSlotKey(incoming);
|
|
5729
|
+
const incomingValue = preferenceValueKey(incoming);
|
|
5730
|
+
const superseded = [];
|
|
5731
|
+
const records = [];
|
|
5732
|
+
let reaffirmed = false;
|
|
5733
|
+
for (const preference of state) {
|
|
5734
|
+
if (!isActiveAt(preference, now)) {
|
|
5735
|
+
records.push(preference);
|
|
5736
|
+
continue;
|
|
5737
|
+
}
|
|
5738
|
+
if (preferenceValueKey(preference) === incomingValue) {
|
|
5739
|
+
records.push({
|
|
5740
|
+
...preference,
|
|
5741
|
+
confidence: Math.max(preference.confidence, incoming.confidence)
|
|
5742
|
+
});
|
|
5743
|
+
reaffirmed = true;
|
|
5744
|
+
continue;
|
|
5745
|
+
}
|
|
5746
|
+
if (preferenceSlotKey(preference) === incomingSlot) {
|
|
5747
|
+
const dead = {
|
|
5748
|
+
...preference,
|
|
5749
|
+
invalidatedAt: now,
|
|
5750
|
+
supersededBy: incoming.id
|
|
5751
|
+
};
|
|
5752
|
+
superseded.push(dead);
|
|
5753
|
+
records.push(dead);
|
|
5754
|
+
continue;
|
|
5755
|
+
}
|
|
5756
|
+
records.push(preference);
|
|
5757
|
+
}
|
|
5758
|
+
if (!reaffirmed) {
|
|
5759
|
+
records.push(incoming);
|
|
5760
|
+
}
|
|
5761
|
+
return { records, superseded };
|
|
5762
|
+
}
|
|
5763
|
+
function mergeAliases(existing, incoming) {
|
|
5764
|
+
const seen = new Set(existing);
|
|
5765
|
+
const merged = [...existing];
|
|
5766
|
+
for (const alias of incoming) {
|
|
5767
|
+
if (!seen.has(alias)) {
|
|
5768
|
+
seen.add(alias);
|
|
5769
|
+
merged.push(alias);
|
|
5770
|
+
}
|
|
5771
|
+
}
|
|
5772
|
+
return merged;
|
|
5773
|
+
}
|
|
5774
|
+
function applyEntity(state, incoming, now) {
|
|
5775
|
+
const incomingSlot = entitySlotKey(incoming);
|
|
5776
|
+
const records = [];
|
|
5777
|
+
let merged = false;
|
|
5778
|
+
for (const entity of state) {
|
|
5779
|
+
if (isActiveAt(entity, now) && entitySlotKey(entity) === incomingSlot) {
|
|
5780
|
+
records.push({
|
|
5781
|
+
...entity,
|
|
5782
|
+
aliases: mergeAliases(entity.aliases, incoming.aliases),
|
|
5783
|
+
confidence: Math.max(entity.confidence, incoming.confidence)
|
|
5784
|
+
});
|
|
5785
|
+
merged = true;
|
|
5786
|
+
continue;
|
|
5787
|
+
}
|
|
5788
|
+
records.push(entity);
|
|
5789
|
+
}
|
|
5790
|
+
if (!merged) {
|
|
5791
|
+
records.push(incoming);
|
|
5792
|
+
}
|
|
5793
|
+
return { records, superseded: [] };
|
|
5794
|
+
}
|
|
5795
|
+
|
|
5796
|
+
// src/memory/fold.ts
|
|
5797
|
+
function emptyState() {
|
|
5798
|
+
return { facts: [], preferences: [], entities: [] };
|
|
5799
|
+
}
|
|
5800
|
+
function applyTombstone(state, tombstone) {
|
|
5801
|
+
const mark = (records) => records.map((record) => record.id === tombstone.id ? {
|
|
5802
|
+
...record,
|
|
5803
|
+
invalidatedAt: tombstone.invalidatedAt,
|
|
5804
|
+
supersededBy: tombstone.supersededBy
|
|
5805
|
+
} : record);
|
|
5806
|
+
if (tombstone.kind === "fact") {
|
|
5807
|
+
state.facts = mark(state.facts);
|
|
5808
|
+
} else if (tombstone.kind === "preference") {
|
|
5809
|
+
state.preferences = mark(state.preferences);
|
|
5810
|
+
} else {
|
|
5811
|
+
state.entities = mark(state.entities);
|
|
5812
|
+
}
|
|
5813
|
+
}
|
|
5814
|
+
function foldAssertions(assertions) {
|
|
5815
|
+
const state = emptyState();
|
|
5816
|
+
for (const assertion of assertions) {
|
|
5817
|
+
if (assertion.invalidatedAt !== null) {
|
|
5818
|
+
applyTombstone(state, assertion);
|
|
5819
|
+
continue;
|
|
5820
|
+
}
|
|
5821
|
+
if (assertion.kind === "fact") {
|
|
5822
|
+
state.facts = applyFact(state.facts, assertion, assertion.createdAt).records;
|
|
5823
|
+
} else if (assertion.kind === "preference") {
|
|
5824
|
+
state.preferences = applyPreference(state.preferences, assertion, assertion.createdAt).records;
|
|
5825
|
+
} else {
|
|
5826
|
+
state.entities = applyEntity(state.entities, assertion, assertion.createdAt).records;
|
|
5827
|
+
}
|
|
5828
|
+
}
|
|
5829
|
+
return state;
|
|
5830
|
+
}
|
|
5831
|
+
function allRecords(state) {
|
|
5832
|
+
return [...state.facts, ...state.preferences, ...state.entities];
|
|
5833
|
+
}
|
|
5834
|
+
|
|
5835
|
+
// src/memory/log.ts
|
|
5836
|
+
function serializeAssertion(record) {
|
|
5837
|
+
return JSON.stringify(record);
|
|
5838
|
+
}
|
|
5839
|
+
function parseMemoryLog(text2) {
|
|
5840
|
+
const records = [];
|
|
5841
|
+
for (const line of text2.split(`
|
|
5842
|
+
`)) {
|
|
5843
|
+
const trimmed = line.trim();
|
|
5844
|
+
if (trimmed.length === 0) {
|
|
5845
|
+
continue;
|
|
5846
|
+
}
|
|
5847
|
+
let candidate;
|
|
5848
|
+
try {
|
|
5849
|
+
candidate = JSON.parse(trimmed);
|
|
5850
|
+
} catch {
|
|
5851
|
+
continue;
|
|
5852
|
+
}
|
|
5853
|
+
const parsed = MemoryRecordSchema.safeParse(candidate);
|
|
5854
|
+
if (parsed.success) {
|
|
5855
|
+
records.push(parsed.data);
|
|
5856
|
+
}
|
|
5857
|
+
}
|
|
5858
|
+
return records;
|
|
5859
|
+
}
|
|
5860
|
+
|
|
5861
|
+
// src/storage/index/memoryConsolidate.ts
|
|
5862
|
+
async function runConsolidation(logPath, deps) {
|
|
5863
|
+
const params = deps.params ?? DEFAULT_DECAY_PARAMS;
|
|
5864
|
+
const text2 = await deps.storage.read(logPath) ?? "";
|
|
5865
|
+
const state = foldAssertions(parseMemoryLog(text2));
|
|
5866
|
+
const tombstones = consolidate(allRecords(state), deps.now, params);
|
|
5867
|
+
if (tombstones.length === 0) {
|
|
5868
|
+
return { forgotten: 0 };
|
|
5869
|
+
}
|
|
5870
|
+
const appended = `${tombstones.map(serializeAssertion).join(`
|
|
5871
|
+
`)}
|
|
5872
|
+
`;
|
|
5873
|
+
await deps.storage.append(logPath, appended);
|
|
5874
|
+
return { forgotten: tombstones.length };
|
|
5875
|
+
}
|
|
5876
|
+
|
|
5877
|
+
// src/local/embeddings.ts
|
|
5878
|
+
function parseEmbeddingsResponse(payload) {
|
|
5879
|
+
if (typeof payload !== "object" || payload === null || !Array.isArray(payload.data)) {
|
|
5880
|
+
throw new Error("Respuesta de /embeddings malformada: falta 'data'");
|
|
5881
|
+
}
|
|
5882
|
+
const data = payload.data;
|
|
5883
|
+
return data.map((entry, index) => {
|
|
5884
|
+
const embedding = typeof entry === "object" && entry !== null ? entry.embedding : undefined;
|
|
5885
|
+
if (!Array.isArray(embedding) || !embedding.every((n) => typeof n === "number" && Number.isFinite(n))) {
|
|
5886
|
+
throw new Error(`Respuesta de /embeddings malformada: vector inválido en índice ${index}`);
|
|
5887
|
+
}
|
|
5888
|
+
return embedding;
|
|
5889
|
+
});
|
|
5890
|
+
}
|
|
5891
|
+
async function embedTexts(request, deps) {
|
|
5892
|
+
if (request.texts.length === 0) {
|
|
5893
|
+
return [];
|
|
5894
|
+
}
|
|
5895
|
+
let response2;
|
|
5896
|
+
try {
|
|
5897
|
+
response2 = await deps.fetch(embeddingsEndpoint(request.baseURL), {
|
|
5898
|
+
method: "POST",
|
|
5899
|
+
headers: {
|
|
5900
|
+
"content-type": "application/json",
|
|
5901
|
+
accept: "application/json"
|
|
5902
|
+
},
|
|
5903
|
+
body: JSON.stringify({
|
|
5904
|
+
model: request.modelID,
|
|
5905
|
+
input: [...request.texts]
|
|
5906
|
+
})
|
|
5907
|
+
});
|
|
5908
|
+
} catch (error) {
|
|
5909
|
+
throw new Error(`Embeddings local falló: ${errorMessage(error)}`);
|
|
5910
|
+
}
|
|
5911
|
+
if (!response2.ok) {
|
|
5912
|
+
throw new Error(`Embeddings local falló: HTTP ${response2.status} en /embeddings`);
|
|
5913
|
+
}
|
|
5914
|
+
let payload;
|
|
5915
|
+
try {
|
|
5916
|
+
payload = await response2.json();
|
|
5917
|
+
} catch (error) {
|
|
5918
|
+
throw new Error(`Embeddings local falló: ${errorMessage(error)}`);
|
|
5919
|
+
}
|
|
5920
|
+
const vectors = parseEmbeddingsResponse(payload);
|
|
5921
|
+
if (vectors.length !== request.texts.length) {
|
|
5922
|
+
throw new Error(`Embeddings local devolvió ${vectors.length} vectores para ${request.texts.length} textos`);
|
|
5923
|
+
}
|
|
5924
|
+
return vectors;
|
|
5925
|
+
}
|
|
5926
|
+
function createLocalEmbedder(request, deps) {
|
|
5927
|
+
return (texts) => embedTexts({ ...request, texts }, deps);
|
|
5928
|
+
}
|
|
5929
|
+
|
|
5930
|
+
// src/storage/index/memoryIndex.ts
|
|
5931
|
+
var SCHEMA2 = `
|
|
5932
|
+
CREATE TABLE IF NOT EXISTS memory_records (
|
|
5933
|
+
id TEXT PRIMARY KEY,
|
|
5934
|
+
kind TEXT NOT NULL,
|
|
5935
|
+
owner_key TEXT NOT NULL,
|
|
5936
|
+
created_at INTEGER NOT NULL,
|
|
5937
|
+
invalidated_at INTEGER,
|
|
5938
|
+
payload TEXT NOT NULL
|
|
5939
|
+
);
|
|
5940
|
+
CREATE INDEX IF NOT EXISTS idx_memory_owner ON memory_records(owner_key, kind);
|
|
5941
|
+
CREATE TABLE IF NOT EXISTS memory_embeddings (
|
|
5942
|
+
record_id TEXT PRIMARY KEY,
|
|
5943
|
+
dim INTEGER NOT NULL,
|
|
5944
|
+
vector TEXT NOT NULL
|
|
5945
|
+
);
|
|
5946
|
+
`;
|
|
5947
|
+
var INSERT_SQL2 = `INSERT OR REPLACE INTO memory_records
|
|
5948
|
+
(id, kind, owner_key, created_at, invalidated_at, payload)
|
|
5949
|
+
VALUES (?, ?, ?, ?, ?, ?)`;
|
|
5950
|
+
var INSERT_EMBEDDING_SQL = `INSERT OR REPLACE INTO memory_embeddings
|
|
5951
|
+
(record_id, dim, vector) VALUES (?, ?, ?)`;
|
|
5952
|
+
function insertRecords(db, records) {
|
|
5953
|
+
const stmt = db.query(INSERT_SQL2);
|
|
5954
|
+
db.run("BEGIN");
|
|
5955
|
+
try {
|
|
5956
|
+
for (const record of records) {
|
|
5957
|
+
stmt.run(record.id, record.kind, record.ownerKey, record.createdAt, record.invalidatedAt, JSON.stringify(record));
|
|
5958
|
+
}
|
|
5959
|
+
db.run("COMMIT");
|
|
5960
|
+
} catch (error) {
|
|
5961
|
+
db.run("ROLLBACK");
|
|
5962
|
+
throw error;
|
|
5963
|
+
}
|
|
5964
|
+
}
|
|
5965
|
+
function createMemoryIndex(db) {
|
|
5966
|
+
db.exec(SCHEMA2);
|
|
5967
|
+
const allRecordsFn = () => {
|
|
5968
|
+
const rows = db.query("SELECT payload FROM memory_records ORDER BY created_at ASC, id ASC").all();
|
|
5969
|
+
const records = [];
|
|
5970
|
+
for (const row of rows) {
|
|
5971
|
+
let candidate;
|
|
5972
|
+
try {
|
|
5973
|
+
candidate = JSON.parse(row.payload);
|
|
5974
|
+
} catch {
|
|
5975
|
+
continue;
|
|
5976
|
+
}
|
|
5977
|
+
const parsed = MemoryRecordSchema.safeParse(candidate);
|
|
5978
|
+
if (parsed.success) {
|
|
5979
|
+
records.push(parsed.data);
|
|
5980
|
+
}
|
|
5981
|
+
}
|
|
5982
|
+
return records;
|
|
5983
|
+
};
|
|
5984
|
+
const state = () => {
|
|
5985
|
+
const facts = [];
|
|
5986
|
+
const preferences = [];
|
|
5987
|
+
const entities = [];
|
|
5988
|
+
for (const record of allRecordsFn()) {
|
|
5989
|
+
if (record.kind === "fact") {
|
|
5990
|
+
facts.push(record);
|
|
5991
|
+
} else if (record.kind === "preference") {
|
|
5992
|
+
preferences.push(record);
|
|
5993
|
+
} else {
|
|
5994
|
+
entities.push(record);
|
|
5995
|
+
}
|
|
5996
|
+
}
|
|
5997
|
+
return { facts, preferences, entities };
|
|
5998
|
+
};
|
|
5999
|
+
return {
|
|
6000
|
+
rebuild(assertions) {
|
|
6001
|
+
const folded = allRecords(foldAssertions(assertions));
|
|
6002
|
+
db.run("DELETE FROM memory_records");
|
|
6003
|
+
insertRecords(db, folded);
|
|
6004
|
+
},
|
|
6005
|
+
allRecords: allRecordsFn,
|
|
6006
|
+
state,
|
|
6007
|
+
activeAt(asOf) {
|
|
6008
|
+
return filterActive(allRecordsFn(), asOf);
|
|
6009
|
+
},
|
|
6010
|
+
putEmbeddings(entries) {
|
|
6011
|
+
const stmt = db.query(INSERT_EMBEDDING_SQL);
|
|
6012
|
+
db.run("BEGIN");
|
|
6013
|
+
try {
|
|
6014
|
+
for (const entry of entries) {
|
|
6015
|
+
stmt.run(entry.recordId, entry.vector.length, JSON.stringify(entry.vector));
|
|
6016
|
+
}
|
|
6017
|
+
db.run("COMMIT");
|
|
6018
|
+
} catch (error) {
|
|
6019
|
+
db.run("ROLLBACK");
|
|
6020
|
+
throw error;
|
|
6021
|
+
}
|
|
6022
|
+
},
|
|
6023
|
+
embeddingMap() {
|
|
6024
|
+
const rows = db.query("SELECT record_id, vector FROM memory_embeddings").all();
|
|
6025
|
+
const map = new Map;
|
|
6026
|
+
for (const row of rows) {
|
|
6027
|
+
try {
|
|
6028
|
+
const vector = JSON.parse(row.vector);
|
|
6029
|
+
if (Array.isArray(vector) && vector.every((n) => typeof n === "number")) {
|
|
6030
|
+
map.set(row.record_id, vector);
|
|
6031
|
+
}
|
|
6032
|
+
} catch {}
|
|
6033
|
+
}
|
|
6034
|
+
return map;
|
|
6035
|
+
},
|
|
6036
|
+
pruneEmbeddings() {
|
|
6037
|
+
const result = db.run("DELETE FROM memory_embeddings WHERE record_id NOT IN (SELECT id FROM memory_records)");
|
|
6038
|
+
return typeof result?.changes === "number" ? result.changes : 0;
|
|
6039
|
+
},
|
|
6040
|
+
close() {
|
|
6041
|
+
db.close();
|
|
6042
|
+
}
|
|
6043
|
+
};
|
|
6044
|
+
}
|
|
6045
|
+
async function rebuildMemoryIndexFromStorage(index, logPath, deps) {
|
|
6046
|
+
const text2 = await deps.storage.read(logPath) ?? "";
|
|
6047
|
+
const assertions = parseMemoryLog(text2);
|
|
6048
|
+
index.rebuild(assertions);
|
|
6049
|
+
return assertions.length;
|
|
6050
|
+
}
|
|
6051
|
+
|
|
6052
|
+
// src/storage/index/memoryRuntime.ts
|
|
6053
|
+
async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
|
|
6054
|
+
const semantic = resolveMemorySemantic(config);
|
|
6055
|
+
if (!semantic.enabled || semantic.embeddings.provider === "none") {
|
|
6056
|
+
return;
|
|
6057
|
+
}
|
|
6058
|
+
if (options.requireInjectionEnabled === true && !semantic.injection.enabled) {
|
|
6059
|
+
return;
|
|
6060
|
+
}
|
|
6061
|
+
const runtime = config.local.runtimes.find((rt) => rt.enabled && rt.baseURL !== undefined);
|
|
6062
|
+
if (runtime?.baseURL === undefined) {
|
|
6063
|
+
return;
|
|
6064
|
+
}
|
|
6065
|
+
const modelID = semantic.embeddings.model?.modelID ?? runtime.defaultModel.modelID;
|
|
6066
|
+
const openDatabase = deps.openDatabase ?? createBunSqliteDatabase;
|
|
6067
|
+
const makeEmbedder = deps.createEmbedder ?? createLocalEmbedder;
|
|
6068
|
+
const fetchImpl = deps.fetch ?? globalThis.fetch;
|
|
6069
|
+
const now = deps.now ?? (() => Date.now());
|
|
6070
|
+
try {
|
|
6071
|
+
const db = await openDatabase(semantic.indexPath);
|
|
6072
|
+
const index = createMemoryIndex(db);
|
|
6073
|
+
await rebuildMemoryIndexFromStorage(index, semantic.logPath, {
|
|
6074
|
+
storage: deps.storage
|
|
6075
|
+
});
|
|
6076
|
+
const embedder = makeEmbedder({ baseURL: runtime.baseURL, modelID }, { fetch: fetchImpl });
|
|
6077
|
+
return {
|
|
6078
|
+
index,
|
|
6079
|
+
embedder,
|
|
6080
|
+
now,
|
|
6081
|
+
ownerKey: semantic.scope === "user" ? "user" : "project",
|
|
6082
|
+
semantic
|
|
6083
|
+
};
|
|
6084
|
+
} catch {
|
|
6085
|
+
return;
|
|
6086
|
+
}
|
|
6087
|
+
}
|
|
6088
|
+
|
|
5261
6089
|
// src/web/git.ts
|
|
5262
6090
|
function createGitLastCommit(exec) {
|
|
5263
6091
|
return async () => {
|
|
@@ -5298,6 +6126,18 @@ async function loadConfig(path) {
|
|
|
5298
6126
|
const content = await storage.read(path);
|
|
5299
6127
|
return content === undefined ? loadOpenTeamConfig({}) : loadOpenTeamConfig(JSON.parse(content));
|
|
5300
6128
|
}
|
|
6129
|
+
async function buildMcpSemanticRecall(config) {
|
|
6130
|
+
const runtime = await buildMemoryRuntimeFromConfig(config, { storage });
|
|
6131
|
+
if (runtime === undefined) {
|
|
6132
|
+
return;
|
|
6133
|
+
}
|
|
6134
|
+
const { index, embedder, now, ownerKey, semantic } = runtime;
|
|
6135
|
+
return buildSemanticRecall({ index, embedder, now }, {
|
|
6136
|
+
ownerKey,
|
|
6137
|
+
minSimilarity: semantic.recall.minSimilarity,
|
|
6138
|
+
includeShared: true
|
|
6139
|
+
});
|
|
6140
|
+
}
|
|
5301
6141
|
async function openBrowser(url) {
|
|
5302
6142
|
if (process.platform === "win32") {
|
|
5303
6143
|
await nodeExec("cmd", ["/c", "start", "", url]);
|
|
@@ -5424,17 +6264,31 @@ async function main() {
|
|
|
5424
6264
|
return;
|
|
5425
6265
|
}
|
|
5426
6266
|
if (argv[0] === "mcp") {
|
|
6267
|
+
const mcpConfig = await loadConfig(DEFAULT_CONFIG_PATH);
|
|
6268
|
+
const recallSemantic = await buildMcpSemanticRecall(mcpConfig);
|
|
5427
6269
|
await runMcpStdio({
|
|
5428
6270
|
readMemory: async () => buildTeamMemory(await readSessionEvents(DEFAULT_SESSIONS_DIR, {
|
|
5429
6271
|
storage,
|
|
5430
6272
|
legacyTelemetryPath: DEFAULT_TELEMETRY_PATH
|
|
5431
|
-
}))
|
|
6273
|
+
})),
|
|
6274
|
+
...recallSemantic === undefined ? {} : { recallSemantic }
|
|
5432
6275
|
}, {
|
|
5433
6276
|
lines: linesFromReadable(process.stdin),
|
|
5434
6277
|
write: (line) => process.stdout.write(line)
|
|
5435
6278
|
});
|
|
5436
6279
|
return;
|
|
5437
6280
|
}
|
|
6281
|
+
if (argv[0] === "memory" && argv[1] === "consolidate") {
|
|
6282
|
+
const memConfig = await loadConfig(DEFAULT_CONFIG_PATH);
|
|
6283
|
+
const semantic = resolveMemorySemantic(memConfig);
|
|
6284
|
+
const result2 = await runConsolidation(semantic.logPath, {
|
|
6285
|
+
storage,
|
|
6286
|
+
now: Date.now()
|
|
6287
|
+
});
|
|
6288
|
+
process.stdout.write(`Consolidación de memoria: ${result2.forgotten} record(s) olvidado(s).
|
|
6289
|
+
`);
|
|
6290
|
+
return;
|
|
6291
|
+
}
|
|
5438
6292
|
const result = await runCli(argv, deps);
|
|
5439
6293
|
process.stdout.write(`${result.stdout}
|
|
5440
6294
|
`);
|