@jmanuelcorral/openteam 0.1.38 → 0.1.41

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 (162) hide show
  1. package/.opencode/command/openteam.md +5 -5
  2. package/AGENTS.md +50 -35
  3. package/README.es.md +759 -0
  4. package/README.md +213 -480
  5. package/dist/capabilities/types.d.ts +1 -1
  6. package/dist/cli/consoleServe.d.ts.map +1 -1
  7. package/dist/cli.js +1123 -232
  8. package/dist/commands/frontierCatalog.d.ts +27 -0
  9. package/dist/commands/frontierCatalog.d.ts.map +1 -0
  10. package/dist/commands/graph.d.ts +65 -0
  11. package/dist/commands/graph.d.ts.map +1 -0
  12. package/dist/commands/orchestratorAgent.d.ts +2 -1
  13. package/dist/commands/orchestratorAgent.d.ts.map +1 -1
  14. package/dist/commands/setup.d.ts +3 -18
  15. package/dist/commands/setup.d.ts.map +1 -1
  16. package/dist/config/load.d.ts.map +1 -1
  17. package/dist/config/migrations.d.ts +27 -0
  18. package/dist/config/migrations.d.ts.map +1 -0
  19. package/dist/config/resolve.d.ts +34 -0
  20. package/dist/config/resolve.d.ts.map +1 -0
  21. package/dist/config/schema.d.ts +235 -0
  22. package/dist/config/schema.d.ts.map +1 -1
  23. package/dist/console/assets.d.ts +9 -0
  24. package/dist/console/assets.d.ts.map +1 -0
  25. package/dist/console/render.d.ts.map +1 -1
  26. package/dist/context/redaction.d.ts +48 -0
  27. package/dist/context/redaction.d.ts.map +1 -0
  28. package/dist/context/schema.d.ts +115 -0
  29. package/dist/context/schema.d.ts.map +1 -0
  30. package/dist/context/types.d.ts +8 -0
  31. package/dist/context/types.d.ts.map +1 -0
  32. package/dist/contract/opencode.d.ts +1 -1
  33. package/dist/contract/opencode.d.ts.map +1 -1
  34. package/dist/graph/events.d.ts +25 -0
  35. package/dist/graph/events.d.ts.map +1 -0
  36. package/dist/graph/ids.d.ts +5 -0
  37. package/dist/graph/ids.d.ts.map +1 -0
  38. package/dist/graph/planner.d.ts +15 -0
  39. package/dist/graph/planner.d.ts.map +1 -0
  40. package/dist/graph/policies.d.ts +34 -0
  41. package/dist/graph/policies.d.ts.map +1 -0
  42. package/dist/graph/privacyPolicy.d.ts +29 -0
  43. package/dist/graph/privacyPolicy.d.ts.map +1 -0
  44. package/dist/graph/reducer.d.ts +27 -0
  45. package/dist/graph/reducer.d.ts.map +1 -0
  46. package/dist/graph/replay.d.ts +20 -0
  47. package/dist/graph/replay.d.ts.map +1 -0
  48. package/dist/graph/reviewerPolicy.d.ts +27 -0
  49. package/dist/graph/reviewerPolicy.d.ts.map +1 -0
  50. package/dist/graph/schema.d.ts +220 -0
  51. package/dist/graph/schema.d.ts.map +1 -0
  52. package/dist/graph/types.d.ts +40 -0
  53. package/dist/graph/types.d.ts.map +1 -0
  54. package/dist/graph/validate.d.ts +19 -0
  55. package/dist/graph/validate.d.ts.map +1 -0
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +873 -88
  58. package/dist/local/embeddings.d.ts +29 -0
  59. package/dist/local/embeddings.d.ts.map +1 -0
  60. package/dist/local/extractClient.d.ts +31 -0
  61. package/dist/local/extractClient.d.ts.map +1 -0
  62. package/dist/local/openai-compatible.d.ts +1 -0
  63. package/dist/local/openai-compatible.d.ts.map +1 -1
  64. package/dist/mcp/semanticRecall.d.ts +25 -0
  65. package/dist/mcp/semanticRecall.d.ts.map +1 -0
  66. package/dist/mcp/server.d.ts +22 -0
  67. package/dist/mcp/server.d.ts.map +1 -1
  68. package/dist/memory/consolidate.d.ts +21 -0
  69. package/dist/memory/consolidate.d.ts.map +1 -0
  70. package/dist/memory/extract.d.ts +68 -0
  71. package/dist/memory/extract.d.ts.map +1 -0
  72. package/dist/memory/fold.d.ts +24 -0
  73. package/dist/memory/fold.d.ts.map +1 -0
  74. package/dist/memory/index.d.ts +18 -0
  75. package/dist/memory/index.d.ts.map +1 -0
  76. package/dist/memory/inject.d.ts +29 -0
  77. package/dist/memory/inject.d.ts.map +1 -0
  78. package/dist/memory/log.d.ts +16 -0
  79. package/dist/memory/log.d.ts.map +1 -0
  80. package/dist/memory/merge.d.ts +38 -0
  81. package/dist/memory/merge.d.ts.map +1 -0
  82. package/dist/memory/rank.d.ts +32 -0
  83. package/dist/memory/rank.d.ts.map +1 -0
  84. package/dist/memory/recall.d.ts +30 -0
  85. package/dist/memory/recall.d.ts.map +1 -0
  86. package/dist/memory/temporal.d.ts +39 -0
  87. package/dist/memory/temporal.d.ts.map +1 -0
  88. package/dist/memory/types.d.ts +159 -0
  89. package/dist/memory/types.d.ts.map +1 -0
  90. package/dist/memory/vector.d.ts +18 -0
  91. package/dist/memory/vector.d.ts.map +1 -0
  92. package/dist/orchestrator/graphCancellation.d.ts +84 -0
  93. package/dist/orchestrator/graphCancellation.d.ts.map +1 -0
  94. package/dist/orchestrator/graphEffects.d.ts +63 -0
  95. package/dist/orchestrator/graphEffects.d.ts.map +1 -0
  96. package/dist/orchestrator/graphReview.d.ts +84 -0
  97. package/dist/orchestrator/graphReview.d.ts.map +1 -0
  98. package/dist/orchestrator/graphRuntime.d.ts +69 -0
  99. package/dist/orchestrator/graphRuntime.d.ts.map +1 -0
  100. package/dist/orchestrator/opencodeSessionAdapter.d.ts +117 -0
  101. package/dist/orchestrator/opencodeSessionAdapter.d.ts.map +1 -0
  102. package/dist/orchestrator/roles.d.ts +1 -1
  103. package/dist/orchestrator/roles.d.ts.map +1 -1
  104. package/dist/orchestrator/sdd.d.ts +38 -2
  105. package/dist/orchestrator/sdd.d.ts.map +1 -1
  106. package/dist/orchestrator/sddCompiler.d.ts +44 -0
  107. package/dist/orchestrator/sddCompiler.d.ts.map +1 -0
  108. package/dist/orchestrator/sessionReconciler.d.ts +74 -0
  109. package/dist/orchestrator/sessionReconciler.d.ts.map +1 -0
  110. package/dist/orchestrator/shadowComparator.d.ts +62 -0
  111. package/dist/orchestrator/shadowComparator.d.ts.map +1 -0
  112. package/dist/plugin/hooks.d.ts +13 -0
  113. package/dist/plugin/hooks.d.ts.map +1 -1
  114. package/dist/plugin/memoryInject.d.ts +25 -0
  115. package/dist/plugin/memoryInject.d.ts.map +1 -0
  116. package/dist/router/chooseModel.d.ts.map +1 -1
  117. package/dist/router/cost.d.ts +4 -0
  118. package/dist/router/cost.d.ts.map +1 -0
  119. package/dist/router/frontierBaseline.d.ts +13 -0
  120. package/dist/router/frontierBaseline.d.ts.map +1 -0
  121. package/dist/router/modelSelection.d.ts +18 -0
  122. package/dist/router/modelSelection.d.ts.map +1 -0
  123. package/dist/storage/graph/codec.d.ts +52 -0
  124. package/dist/storage/graph/codec.d.ts.map +1 -0
  125. package/dist/storage/graph/fsGraphJournal.d.ts +3 -0
  126. package/dist/storage/graph/fsGraphJournal.d.ts.map +1 -0
  127. package/dist/storage/graph/importLegacy.d.ts +62 -0
  128. package/dist/storage/graph/importLegacy.d.ts.map +1 -0
  129. package/dist/storage/graph/memoryGraphJournal.d.ts +11 -0
  130. package/dist/storage/graph/memoryGraphJournal.d.ts.map +1 -0
  131. package/dist/storage/graph/migrateLegacyBriefs.d.ts +32 -0
  132. package/dist/storage/graph/migrateLegacyBriefs.d.ts.map +1 -0
  133. package/dist/storage/graph/projections.d.ts +31 -0
  134. package/dist/storage/graph/projections.d.ts.map +1 -0
  135. package/dist/storage/graph/provider.d.ts +52 -0
  136. package/dist/storage/graph/provider.d.ts.map +1 -0
  137. package/dist/storage/graph/recovery.d.ts +38 -0
  138. package/dist/storage/graph/recovery.d.ts.map +1 -0
  139. package/dist/storage/graph/runRegistry.d.ts +17 -0
  140. package/dist/storage/graph/runRegistry.d.ts.map +1 -0
  141. package/dist/storage/graph/snapshot.d.ts +44 -0
  142. package/dist/storage/graph/snapshot.d.ts.map +1 -0
  143. package/dist/storage/graph/workspaceLease.d.ts +36 -0
  144. package/dist/storage/graph/workspaceLease.d.ts.map +1 -0
  145. package/dist/storage/graph/writer.d.ts +20 -0
  146. package/dist/storage/graph/writer.d.ts.map +1 -0
  147. package/dist/storage/index/memoryConsolidate.d.ts +24 -0
  148. package/dist/storage/index/memoryConsolidate.d.ts.map +1 -0
  149. package/dist/storage/index/memoryIndex.d.ts +52 -0
  150. package/dist/storage/index/memoryIndex.d.ts.map +1 -0
  151. package/dist/storage/index/memoryRecall.d.ts +23 -0
  152. package/dist/storage/index/memoryRecall.d.ts.map +1 -0
  153. package/dist/storage/index/memoryRuntime.d.ts +41 -0
  154. package/dist/storage/index/memoryRuntime.d.ts.map +1 -0
  155. package/dist/telemetry/events.d.ts +3 -1
  156. package/dist/telemetry/events.d.ts.map +1 -1
  157. package/dist/telemetry/graphProjection.d.ts +36 -0
  158. package/dist/telemetry/graphProjection.d.ts.map +1 -0
  159. package/dist/telemetry/hash.d.ts.map +1 -1
  160. package/dist/web/server.d.ts +4 -0
  161. package/dist/web/server.d.ts.map +1 -1
  162. package/package.json +14 -6
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ import { createRequire } from "node:module";
2
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
+
1
4
  // src/index.ts
2
5
  import { dirname as dirname2 } from "node:path";
3
6
 
@@ -21,7 +24,7 @@ function buildOrchestratorAgent(frontier, options = {}) {
21
24
  ];
22
25
  const frontmatter = [
23
26
  "---",
24
- 'description: "Orquestador openteam estilo Squad: 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."',
27
+ '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."',
25
28
  "mode: primary",
26
29
  `model: ${model}`,
27
30
  "temperature: 0.2",
@@ -30,7 +33,7 @@ function buildOrchestratorAgent(frontier, options = {}) {
30
33
  ].join(`
31
34
  `);
32
35
  const body = [
33
- "Eres el orquestador de **openteam**, al estilo Squad. Eres el único agente",
36
+ "Eres el orquestador multiagente de **openteam**. Eres el único agente",
34
37
  "primario: no existe un equipo precreado. Tú **construyes el equipo de",
35
38
  "subagentes a demanda** según el objetivo del usuario y del repositorio.",
36
39
  "",
@@ -56,7 +59,7 @@ function buildOrchestratorAgent(frontier, options = {}) {
56
59
  "",
57
60
  "## Casting: elige un universo y nombra al equipo",
58
61
  "",
59
- "Como Squad, el equipo tiene una identidad temática persistente:",
62
+ "El equipo tiene una identidad temática persistente:",
60
63
  "",
61
64
  "1. Al crear el equipo por primera vez, **elige un universo** temático (p. ej.",
62
65
  " una película, serie, cómic o mitología). Si el usuario tiene preferencia,",
@@ -333,6 +336,11 @@ var PrivacyModeSchema = z.enum([
333
336
  "off"
334
337
  ]);
335
338
  var BaselineModeSchema = z.enum(["auto", "pinned"]);
339
+ var GraphModeSchema = z.enum(["off", "shadow", "active"]);
340
+ var GraphConfigSchema = z.object({
341
+ mode: GraphModeSchema.default("off"),
342
+ killSwitch: z.boolean().default(false)
343
+ });
336
344
  var ConsoleHostSchema = z.enum(["127.0.0.1", "localhost"]);
337
345
  var ConsoleConfigSchema = z.object({
338
346
  host: ConsoleHostSchema.default("127.0.0.1"),
@@ -379,6 +387,50 @@ var LocalRuntimeSchema = z.object({
379
387
  discovery: z.enum(["cli", "sdk", "manual"]).optional(),
380
388
  defaultModel: ModelRefSchema
381
389
  });
390
+ var MemoryScopeSchema = z.enum(["project", "user"]);
391
+ var MemoryExtractionModeSchema = z.enum([
392
+ "local",
393
+ "localWithFrontierFallback"
394
+ ]);
395
+ var MemoryEmbeddingsProviderSchema = z.enum(["local", "none"]);
396
+ var MemorySemanticSchema = z.object({
397
+ enabled: z.boolean().default(false),
398
+ storeContent: z.boolean().default(false),
399
+ logPath: z.string().min(1).default(".opencode/openteam/memory/records.jsonl"),
400
+ indexPath: z.string().min(1).default(".opencode/openteam/memory/index.sqlite"),
401
+ scope: MemoryScopeSchema.default("project"),
402
+ extraction: z.object({
403
+ mode: MemoryExtractionModeSchema.default("local"),
404
+ model: ModelRefSchema.nullable().default(null)
405
+ }).default({ mode: "local", model: null }),
406
+ embeddings: z.object({
407
+ provider: MemoryEmbeddingsProviderSchema.default("local"),
408
+ model: ModelRefSchema.nullable().default(null),
409
+ dim: z.number().int().positive().default(768)
410
+ }).default({ provider: "local", model: null, dim: 768 }),
411
+ recall: z.object({
412
+ maxResults: z.number().int().positive().default(8),
413
+ minSimilarity: z.number().min(0).max(1).default(0.2)
414
+ }).default({ maxResults: 8, minSimilarity: 0.2 }),
415
+ injection: z.object({
416
+ enabled: z.boolean().default(false),
417
+ maxChars: z.number().int().positive().default(1200),
418
+ maxItems: z.number().int().positive().default(8)
419
+ }).default({ enabled: false, maxChars: 1200, maxItems: 8 })
420
+ }).default({
421
+ enabled: false,
422
+ storeContent: false,
423
+ logPath: ".opencode/openteam/memory/records.jsonl",
424
+ indexPath: ".opencode/openteam/memory/index.sqlite",
425
+ scope: "project",
426
+ extraction: { mode: "local", model: null },
427
+ embeddings: { provider: "local", model: null, dim: 768 },
428
+ recall: { maxResults: 8, minSimilarity: 0.2 },
429
+ injection: { enabled: false, maxChars: 1200, maxItems: 8 }
430
+ });
431
+ var MemoryConfigSchema = z.object({
432
+ semantic: MemorySemanticSchema
433
+ });
382
434
  var OpenTeamConfigObjectSchema = z.object({
383
435
  baseline: z.object({
384
436
  mode: BaselineModeSchema.default("auto"),
@@ -429,9 +481,14 @@ var OpenTeamConfigObjectSchema = z.object({
429
481
  }).default({ hardStopOnBudgetExhaustion: false }),
430
482
  privacyMode: PrivacyModeSchema.default("forceLocalOnSensitive"),
431
483
  console: ConsoleConfigSchema,
432
- storage: StorageConfigSchema
484
+ storage: StorageConfigSchema,
485
+ memory: MemoryConfigSchema.optional(),
486
+ graph: GraphConfigSchema.optional()
433
487
  });
434
488
  var OpenTeamConfigSchema = OpenTeamConfigObjectSchema;
489
+ function resolveMemorySemantic(config) {
490
+ return config.memory?.semantic ?? MemorySemanticSchema.parse(undefined);
491
+ }
435
492
 
436
493
  // src/config/persist.ts
437
494
  var DEFAULT_CONFIG_PATH = ".opencode/openteam.json";
@@ -447,9 +504,40 @@ async function writeOpenTeamConfigFile(config, path = DEFAULT_CONFIG_PATH, stora
447
504
  // src/commands/setup.ts
448
505
  var OPENCODE_CONFIG_PATH = "opencode.json";
449
506
 
507
+ // src/config/migrations.ts
508
+ var CURRENT_CONFIG_VERSION = 1;
509
+
510
+ class ConfigMigrationError extends Error {
511
+ code;
512
+ detail;
513
+ constructor(code, detail) {
514
+ super(`${code}: ${detail}`);
515
+ this.name = "ConfigMigrationError";
516
+ this.code = code;
517
+ this.detail = detail;
518
+ }
519
+ }
520
+ function migrateConfig(raw) {
521
+ if (typeof raw !== "object" || raw === null) {
522
+ return raw;
523
+ }
524
+ const record = raw;
525
+ const version = record.configVersion;
526
+ if (version === undefined) {
527
+ return { ...record, configVersion: CURRENT_CONFIG_VERSION };
528
+ }
529
+ if (typeof version !== "number" || !Number.isInteger(version) || version < 1) {
530
+ throw new ConfigMigrationError("unknown-version", String(version));
531
+ }
532
+ if (version > CURRENT_CONFIG_VERSION) {
533
+ throw new ConfigMigrationError("unsupported-version", String(version));
534
+ }
535
+ return record;
536
+ }
537
+
450
538
  // src/config/load.ts
451
539
  function loadOpenTeamConfig(raw) {
452
- return OpenTeamConfigSchema.parse(raw ?? {});
540
+ return OpenTeamConfigSchema.parse(migrateConfig(raw ?? {}));
453
541
  }
454
542
 
455
543
  // src/local/openai-compatible.ts
@@ -459,6 +547,9 @@ function normalizeBaseURL(baseURL) {
459
547
  function modelsEndpoint(baseURL) {
460
548
  return `${normalizeBaseURL(baseURL)}/models`;
461
549
  }
550
+ function embeddingsEndpoint(baseURL) {
551
+ return `${normalizeBaseURL(baseURL)}/embeddings`;
552
+ }
462
553
  function errorMessage(error) {
463
554
  if (error instanceof Error) {
464
555
  return error.message;
@@ -1562,9 +1653,9 @@ var HELP = [
1562
1653
  " openteam local on Reactiva el routing local-first",
1563
1654
  " openteam doctor Diagnóstico de runtimes y config",
1564
1655
  " openteam agents Lista los agentes y el LLM (local/frontier) de cada uno",
1565
- " openteam console Lanza la Console web multi-sesión (Ctrl+C para parar; --open abre el navegador)",
1566
- " openteam console --status Muestra la config de la Console sin lanzarla",
1567
- " openteam tunnel Expone la Console vía Dev Tunnels (--terminal expone opencode; Ctrl+C para cerrar)",
1656
+ " openteam console [alfa] Lanza la Console web multi-sesión (Ctrl+C para parar; --open abre el navegador)",
1657
+ " openteam console --status [alfa] Muestra la config de la Console sin lanzarla",
1658
+ " openteam tunnel [alfa] Expone la Console vía Dev Tunnels (--terminal expone opencode; Ctrl+C para cerrar)",
1568
1659
  " openteam mcp Servidor MCP de solo lectura (memoria del equipo) sobre stdio",
1569
1660
  " openteam report Resumen de coste/ahorro (telemetría)",
1570
1661
  " openteam yolo status Muestra si el modo YOLO está activo",
@@ -1942,6 +2033,85 @@ function classifyHeuristic(task) {
1942
2033
  };
1943
2034
  }
1944
2035
 
2036
+ // src/router/modelSelection.ts
2037
+ function modelKey2(model) {
2038
+ return `${model.providerID}/${model.modelID}`;
2039
+ }
2040
+ function sameModel(left, right) {
2041
+ return left.providerID === right.providerID && left.modelID === right.modelID;
2042
+ }
2043
+ function compareModels(left, right) {
2044
+ return modelKey2(left).localeCompare(modelKey2(right));
2045
+ }
2046
+ function uniqueModelSelections(models) {
2047
+ const byKey = new Map;
2048
+ for (const model of models) {
2049
+ byKey.set(modelKey2(model), model);
2050
+ }
2051
+ return [...byKey.values()].sort(compareModels);
2052
+ }
2053
+ function localDefault(config) {
2054
+ return config.local.runtimes.find((runtime) => runtime.enabled)?.defaultModel ?? config.router.localDefault;
2055
+ }
2056
+ function resolveConfiguredFrontierBaseline(config) {
2057
+ if (config.baseline.mode === "pinned" && config.baseline.pinnedModel !== null) {
2058
+ return config.baseline.pinnedModel;
2059
+ }
2060
+ return config.baseline.hardDefault;
2061
+ }
2062
+ function compareAvailableModels2(left, right) {
2063
+ return compareModels(left, right);
2064
+ }
2065
+ function defaultAvailableModels2(config) {
2066
+ return [
2067
+ {
2068
+ ...localDefault(config),
2069
+ kind: "local",
2070
+ available: true
2071
+ },
2072
+ {
2073
+ ...resolveConfiguredFrontierBaseline(config),
2074
+ kind: "frontier",
2075
+ available: true
2076
+ }
2077
+ ].sort(compareAvailableModels2);
2078
+ }
2079
+ function availableModels(input) {
2080
+ return (input.availableModels ?? defaultAvailableModels2(input.config)).slice().sort(compareAvailableModels2);
2081
+ }
2082
+ function listedModel(input, model) {
2083
+ return availableModels(input).find((candidate) => sameModel(candidate, model));
2084
+ }
2085
+ function frontierAvailable(input, model) {
2086
+ return listedModel(input, model)?.available !== false;
2087
+ }
2088
+ function localCandidateIsUsable(input, model) {
2089
+ return model.kind === "local" && model.available && !(input.task.requiresTools === true && model.supportsTools === false);
2090
+ }
2091
+ function availableLocalFallbacks(input, selected) {
2092
+ return uniqueModelSelections(availableModels(input).filter((model) => localCandidateIsUsable(input, model)).filter((model) => !sameModel(model, selected)).map((model) => ({
2093
+ providerID: model.providerID,
2094
+ modelID: model.modelID
2095
+ })));
2096
+ }
2097
+
2098
+ // src/router/cost.ts
2099
+ function modelProfile(input, model) {
2100
+ return input.profiles?.find((profile) => sameModel(profile.ref, model));
2101
+ }
2102
+ function estimatedCostUSD(input, model) {
2103
+ const profile = modelProfile(input, model);
2104
+ if (profile === undefined) {
2105
+ return 0;
2106
+ }
2107
+ const inputTokens = input.task.estimatedInputTokens ?? 0;
2108
+ const outputTokens = input.task.estimatedOutputTokens ?? 0;
2109
+ return inputTokens / 1e6 * profile.costPer1M.inputUSD + outputTokens / 1e6 * profile.costPer1M.outputUSD;
2110
+ }
2111
+ function roundUSD(value) {
2112
+ return Math.round(value * 1000000000000) / 1000000000000;
2113
+ }
2114
+
1945
2115
  // src/capabilities/select.ts
1946
2116
  var BLENDED_COST_INPUT_WEIGHT = 0.75;
1947
2117
  var BLENDED_COST_OUTPUT_WEIGHT = 1 - BLENDED_COST_INPUT_WEIGHT;
@@ -1973,7 +2143,7 @@ function deriveRequirement(tier, task) {
1973
2143
  function blendedCostPer1M(profile) {
1974
2144
  return profile.costPer1M.inputUSD * BLENDED_COST_INPUT_WEIGHT + profile.costPer1M.outputUSD * BLENDED_COST_OUTPUT_WEIGHT;
1975
2145
  }
1976
- function modelKey2(profile) {
2146
+ function modelKey3(profile) {
1977
2147
  return `${profile.ref.providerID}/${profile.ref.modelID}`;
1978
2148
  }
1979
2149
  function compareCapableFrontier(req, left, right) {
@@ -1989,7 +2159,7 @@ function compareCapableFrontier(req, left, right) {
1989
2159
  if (reasoningWasteDelta !== 0) {
1990
2160
  return reasoningWasteDelta;
1991
2161
  }
1992
- return modelKey2(left).localeCompare(modelKey2(right));
2162
+ return modelKey3(left).localeCompare(modelKey3(right));
1993
2163
  }
1994
2164
  function satisfiesRequirement(req, profile) {
1995
2165
  return profile.kind === "frontier" && profile.availability !== "unavailable" && profile.contextWindow >= req.minContextWindow && profile.reasoningTier >= req.minReasoningTier && profile.codeQualityTier >= req.minCodeQualityTier && (!req.needsTools || profile.supportsToolCalling) && (!req.needsVision || profile.supportsVision);
@@ -1999,13 +2169,7 @@ function selectCheapestCapableFrontier(req, profiles) {
1999
2169
  return selected?.ref ?? null;
2000
2170
  }
2001
2171
 
2002
- // src/router/chooseModel.ts
2003
- function resolveConfiguredFrontierBaseline(config) {
2004
- if (config.baseline.mode === "pinned" && config.baseline.pinnedModel !== null) {
2005
- return config.baseline.pinnedModel;
2006
- }
2007
- return config.baseline.hardDefault;
2008
- }
2172
+ // src/router/frontierBaseline.ts
2009
2173
  function resolveFrontierBaseline(input, tier) {
2010
2174
  if (input.config.baseline.mode === "pinned" && input.config.baseline.pinnedModel !== null) {
2011
2175
  return { model: input.config.baseline.pinnedModel, rationale: [] };
@@ -2025,48 +2189,8 @@ function resolveFrontierBaseline(input, tier) {
2025
2189
  }
2026
2190
  return { model: input.config.baseline.hardDefault, rationale: [] };
2027
2191
  }
2028
- function localDefault(config) {
2029
- return config.local.runtimes.find((runtime) => runtime.enabled)?.defaultModel ?? config.router.localDefault;
2030
- }
2031
- function modelKey3(model) {
2032
- return `${model.providerID}/${model.modelID}`;
2033
- }
2034
- function sameModel(left, right) {
2035
- return left.providerID === right.providerID && left.modelID === right.modelID;
2036
- }
2037
- function compareModels(left, right) {
2038
- return modelKey3(left).localeCompare(modelKey3(right));
2039
- }
2040
- function uniqueModelSelections(models) {
2041
- const byKey = new Map;
2042
- for (const model of models) {
2043
- byKey.set(modelKey3(model), model);
2044
- }
2045
- return [...byKey.values()].sort(compareModels);
2046
- }
2047
- function defaultAvailableModels2(config) {
2048
- return [
2049
- {
2050
- ...localDefault(config),
2051
- kind: "local",
2052
- available: true
2053
- },
2054
- {
2055
- ...resolveConfiguredFrontierBaseline(config),
2056
- kind: "frontier",
2057
- available: true
2058
- }
2059
- ].sort(compareAvailableModels2);
2060
- }
2061
- function compareAvailableModels2(left, right) {
2062
- return compareModels(left, right);
2063
- }
2064
- function availableModels(input) {
2065
- return (input.availableModels ?? defaultAvailableModels2(input.config)).slice().sort(compareAvailableModels2);
2066
- }
2067
- function listedModel(input, model) {
2068
- return availableModels(input).find((candidate) => sameModel(candidate, model));
2069
- }
2192
+
2193
+ // src/router/chooseModel.ts
2070
2194
  function localStatus(input, model) {
2071
2195
  const listed = listedModel(input, model);
2072
2196
  return {
@@ -2074,18 +2198,6 @@ function localStatus(input, model) {
2074
2198
  missingTools: input.task.requiresTools === true && listed?.supportsTools === false
2075
2199
  };
2076
2200
  }
2077
- function frontierAvailable(input, model) {
2078
- return listedModel(input, model)?.available !== false;
2079
- }
2080
- function localCandidateIsUsable(input, model) {
2081
- return model.kind === "local" && model.available && !(input.task.requiresTools === true && model.supportsTools === false);
2082
- }
2083
- function availableLocalFallbacks(input, selected) {
2084
- return uniqueModelSelections(availableModels(input).filter((model) => localCandidateIsUsable(input, model)).filter((model) => !sameModel(model, selected)).map((model) => ({
2085
- providerID: model.providerID,
2086
- modelID: model.modelID
2087
- })));
2088
- }
2089
2201
  function buildFallbackChain(input, selected, frontier, allowFrontier) {
2090
2202
  const chain = availableLocalFallbacks(input, selected);
2091
2203
  if (allowFrontier && !sameModel(frontier, selected) && frontierAvailable(input, frontier)) {
@@ -2141,21 +2253,6 @@ function resolveFrontierPrimary(input, frontier, rationale) {
2141
2253
  function tierForInput(input) {
2142
2254
  return input.tier ?? classifyHeuristic(input.task).tier;
2143
2255
  }
2144
- function modelProfile(input, model) {
2145
- return input.profiles?.find((profile) => sameModel(profile.ref, model));
2146
- }
2147
- function estimatedCostUSD(input, model) {
2148
- const profile = modelProfile(input, model);
2149
- if (profile === undefined) {
2150
- return 0;
2151
- }
2152
- const inputTokens = input.task.estimatedInputTokens ?? 0;
2153
- const outputTokens = input.task.estimatedOutputTokens ?? 0;
2154
- return inputTokens / 1e6 * profile.costPer1M.inputUSD + outputTokens / 1e6 * profile.costPer1M.outputUSD;
2155
- }
2156
- function roundUSD(value) {
2157
- return Math.round(value * 1000000000000) / 1000000000000;
2158
- }
2159
2256
  function finalizeDecision(input, tier, frontier, budgetAction, core) {
2160
2257
  const selectedCostUSD = estimatedCostUSD(input, core.selected);
2161
2258
  const baselineCostUSD = estimatedCostUSD(input, frontier.model);
@@ -2482,10 +2579,22 @@ function deriveTaskSignals(input, output) {
2482
2579
  }
2483
2580
  return signals;
2484
2581
  }
2582
+ function injectMemoryBlock(output, block) {
2583
+ for (const part of output.parts) {
2584
+ if (part.type === "text" && typeof part.text === "string") {
2585
+ part.text = `${block}
2586
+
2587
+ ${part.text}`;
2588
+ return true;
2589
+ }
2590
+ }
2591
+ return false;
2592
+ }
2485
2593
  function createChatMessageHook(config, getAvailable, options = {}) {
2486
2594
  const sink = options.sink ?? createNullEventSink();
2487
2595
  const now = options.now ?? Date.now;
2488
2596
  return async (input, output) => {
2597
+ const prompt = promptText(output);
2489
2598
  const signals = deriveTaskSignals(input, output);
2490
2599
  const decision2 = chooseModel({
2491
2600
  config,
@@ -2495,7 +2604,7 @@ function createChatMessageHook(config, getAvailable, options = {}) {
2495
2604
  output.message.model = decision2.selected;
2496
2605
  const telemetryContext = {
2497
2606
  ts: now(),
2498
- promptHash: hashPrompt(promptText(output)),
2607
+ promptHash: hashPrompt(prompt),
2499
2608
  promptChars: signals.promptChars,
2500
2609
  sessionID: input.sessionID ?? output.message.sessionID
2501
2610
  };
@@ -2506,6 +2615,14 @@ function createChatMessageHook(config, getAvailable, options = {}) {
2506
2615
  telemetryContext.tokensOut = signals.estimatedOutputTokens;
2507
2616
  }
2508
2617
  emitTelemetry(sink, costRecordToRouteEvent(toCostRecord(decision2, telemetryContext)));
2618
+ if (options.injectMemory !== undefined) {
2619
+ try {
2620
+ const block = await options.injectMemory(prompt);
2621
+ if (block !== null && block.length > 0) {
2622
+ injectMemoryBlock(output, block);
2623
+ }
2624
+ } catch {}
2625
+ }
2509
2626
  };
2510
2627
  }
2511
2628
  function createHooks(config, getAvailable, options = {}) {
@@ -2514,6 +2631,247 @@ function createHooks(config, getAvailable, options = {}) {
2514
2631
  };
2515
2632
  }
2516
2633
 
2634
+ // src/memory/inject.ts
2635
+ var DEFAULT_INJECTION_PARAMS = {
2636
+ maxChars: 1200,
2637
+ header: "Contexto de memoria de openteam (solo referencia):",
2638
+ maxItems: 8
2639
+ };
2640
+ function formatRecordLine(record) {
2641
+ if (record.kind === "fact") {
2642
+ const base = `- ${record.subject} ${record.predicate} ${record.object}`;
2643
+ return record.category === null ? base : `${base} (${record.category})`;
2644
+ }
2645
+ if (record.kind === "preference") {
2646
+ const base = `- [${record.category}] ${record.preference}`;
2647
+ return record.context === null ? base : `${base} — ${record.context}`;
2648
+ }
2649
+ const aliases = record.aliases.length > 0 ? ` (alias: ${record.aliases.join(", ")})` : "";
2650
+ return `- ${record.canonicalName} [${record.type}]${aliases}`;
2651
+ }
2652
+ function buildMemoryBlock(results, params = DEFAULT_INJECTION_PARAMS) {
2653
+ if (results.length === 0) {
2654
+ return null;
2655
+ }
2656
+ const lines = [];
2657
+ let used = params.header.length;
2658
+ for (const result of results) {
2659
+ if (lines.length >= params.maxItems) {
2660
+ break;
2661
+ }
2662
+ const line = formatRecordLine(result.record);
2663
+ const cost = line.length + 1;
2664
+ if (used + cost > params.maxChars) {
2665
+ break;
2666
+ }
2667
+ lines.push(line);
2668
+ used += cost;
2669
+ }
2670
+ if (lines.length === 0) {
2671
+ return null;
2672
+ }
2673
+ return `${params.header}
2674
+ ${lines.join(`
2675
+ `)}`;
2676
+ }
2677
+
2678
+ // src/memory/types.ts
2679
+ import { z as z5 } from "zod";
2680
+ var SHARED_OWNER_KEY = "*";
2681
+ var OwnerKeySchema = z5.string().min(1);
2682
+ var MemoryKindSchema = z5.enum(["fact", "preference", "entity"]);
2683
+ var MemoryBaseSchema = z5.object({
2684
+ id: z5.string().min(1),
2685
+ ownerKey: OwnerKeySchema,
2686
+ confidence: z5.number().min(0).max(1),
2687
+ validFrom: z5.number().finite(),
2688
+ validUntil: z5.number().finite().nullable().default(null),
2689
+ createdAt: z5.number().finite(),
2690
+ invalidatedAt: z5.number().finite().nullable().default(null),
2691
+ sourceHash: z5.string().min(1),
2692
+ supersededBy: z5.string().min(1).nullable().default(null)
2693
+ });
2694
+ var FactSchema = MemoryBaseSchema.extend({
2695
+ kind: z5.literal("fact"),
2696
+ subject: z5.string().min(1),
2697
+ predicate: z5.string().min(1),
2698
+ object: z5.string().min(1),
2699
+ category: z5.string().min(1).nullable().default(null)
2700
+ });
2701
+ var PreferenceSchema = MemoryBaseSchema.extend({
2702
+ kind: z5.literal("preference"),
2703
+ category: z5.string().min(1),
2704
+ preference: z5.string().min(1),
2705
+ context: z5.string().min(1).nullable().default(null),
2706
+ lastAccessedAt: z5.number().finite().nullable().default(null),
2707
+ accessCount: z5.number().int().min(0).default(0)
2708
+ });
2709
+ var EntitySchema = MemoryBaseSchema.extend({
2710
+ kind: z5.literal("entity"),
2711
+ canonicalName: z5.string().min(1),
2712
+ type: z5.string().min(1),
2713
+ aliases: z5.array(z5.string().min(1)).default([])
2714
+ });
2715
+ var MemoryRecordSchema = z5.discriminatedUnion("kind", [
2716
+ FactSchema,
2717
+ PreferenceSchema,
2718
+ EntitySchema
2719
+ ]);
2720
+ var RecallQuerySchema = z5.object({
2721
+ ownerKey: OwnerKeySchema,
2722
+ kinds: z5.array(MemoryKindSchema).default(["fact", "preference", "entity"]),
2723
+ asOf: z5.number().finite().nullable().default(null),
2724
+ limit: z5.number().int().positive().default(8),
2725
+ minSimilarity: z5.number().min(0).max(1).default(0.2),
2726
+ includeShared: z5.boolean().default(true)
2727
+ });
2728
+
2729
+ // src/memory/rank.ts
2730
+ var DAY_MS = 86400000;
2731
+ var DEFAULT_RANK_SIGNALS = {
2732
+ similarityWeight: 1,
2733
+ recencyWeight: 0.2,
2734
+ accessWeight: 0.1,
2735
+ recencyHalfLifeMs: 14 * DAY_MS
2736
+ };
2737
+ function recencyTimestamp(record) {
2738
+ if (record.kind === "preference" && record.lastAccessedAt !== null) {
2739
+ return record.lastAccessedAt;
2740
+ }
2741
+ return record.createdAt;
2742
+ }
2743
+ function recencyScore(record, signals) {
2744
+ const ageMs = Math.max(0, signals.now - recencyTimestamp(record));
2745
+ return 0.5 ** (ageMs / signals.recencyHalfLifeMs);
2746
+ }
2747
+ function accessScore(record) {
2748
+ if (record.kind !== "preference") {
2749
+ return 0;
2750
+ }
2751
+ return 1 - 1 / (1 + record.accessCount);
2752
+ }
2753
+ function scoreCandidate(candidate, signals) {
2754
+ return signals.similarityWeight * candidate.similarity + signals.recencyWeight * recencyScore(candidate.record, signals) + signals.accessWeight * accessScore(candidate.record);
2755
+ }
2756
+ function rankRecall(candidates, signals) {
2757
+ return candidates.map((candidate) => ({
2758
+ ...candidate,
2759
+ score: scoreCandidate(candidate, signals)
2760
+ })).sort((a, b) => {
2761
+ if (b.score !== a.score) {
2762
+ return b.score - a.score;
2763
+ }
2764
+ return a.record.id < b.record.id ? -1 : a.record.id > b.record.id ? 1 : 0;
2765
+ });
2766
+ }
2767
+
2768
+ // src/memory/temporal.ts
2769
+ function isTransactionActiveAt(record, asOf) {
2770
+ return record.invalidatedAt === null || record.invalidatedAt > asOf;
2771
+ }
2772
+ function isValidAt(record, asOf) {
2773
+ if (record.validFrom > asOf) {
2774
+ return false;
2775
+ }
2776
+ return record.validUntil === null || record.validUntil > asOf;
2777
+ }
2778
+ function isActiveAt(record, asOf) {
2779
+ return isTransactionActiveAt(record, asOf) && isValidAt(record, asOf);
2780
+ }
2781
+ function filterActive(records, asOf) {
2782
+ return records.filter((record) => isActiveAt(record, asOf));
2783
+ }
2784
+ function coalesceWindow(a, b) {
2785
+ const validFrom = Math.min(a.validFrom, b.validFrom);
2786
+ const validUntil = a.validUntil === null || b.validUntil === null ? null : Math.max(a.validUntil, b.validUntil);
2787
+ return { validFrom, validUntil };
2788
+ }
2789
+
2790
+ // src/memory/vector.ts
2791
+ function dot(a, b) {
2792
+ if (a.length !== b.length) {
2793
+ throw new Error(`dimension mismatch: ${a.length} vs ${b.length}`);
2794
+ }
2795
+ let sum = 0;
2796
+ for (let i = 0;i < a.length; i += 1) {
2797
+ sum += a[i] * b[i];
2798
+ }
2799
+ return sum;
2800
+ }
2801
+ function norm(a) {
2802
+ let sum = 0;
2803
+ for (let i = 0;i < a.length; i += 1) {
2804
+ const value = a[i];
2805
+ sum += value * value;
2806
+ }
2807
+ return Math.sqrt(sum);
2808
+ }
2809
+ function cosineSimilarity(a, b) {
2810
+ const denom = norm(a) * norm(b);
2811
+ if (denom === 0) {
2812
+ return 0;
2813
+ }
2814
+ return dot(a, b) / denom;
2815
+ }
2816
+
2817
+ // src/memory/recall.ts
2818
+ function matchesScope(record, query) {
2819
+ if (record.ownerKey === query.ownerKey) {
2820
+ return true;
2821
+ }
2822
+ return query.includeShared && record.ownerKey === SHARED_OWNER_KEY;
2823
+ }
2824
+ function recall(records, queryEmbedding, query, deps) {
2825
+ const asOf = query.asOf ?? deps.now;
2826
+ const kinds = new Set(query.kinds);
2827
+ const candidates = [];
2828
+ for (const record of filterActive(records, asOf)) {
2829
+ if (!kinds.has(record.kind) || !matchesScope(record, query)) {
2830
+ continue;
2831
+ }
2832
+ const embedding = deps.embeddings.get(record.id);
2833
+ if (embedding === undefined) {
2834
+ continue;
2835
+ }
2836
+ const similarity = cosineSimilarity(queryEmbedding, embedding);
2837
+ if (similarity < query.minSimilarity) {
2838
+ continue;
2839
+ }
2840
+ candidates.push({ record, similarity });
2841
+ }
2842
+ const signals = {
2843
+ now: deps.now,
2844
+ ...deps.signals ?? DEFAULT_RANK_SIGNALS
2845
+ };
2846
+ return rankRecall(candidates, signals).slice(0, query.limit);
2847
+ }
2848
+
2849
+ // src/storage/index/memoryRecall.ts
2850
+ async function recallMemory(deps, queryText, query) {
2851
+ const [queryEmbedding] = await deps.embedder([queryText]);
2852
+ if (queryEmbedding === undefined) {
2853
+ return [];
2854
+ }
2855
+ const records = deps.index.allRecords();
2856
+ const embeddings = deps.index.embeddingMap();
2857
+ const recallDeps = deps.signals === undefined ? { embeddings, now: deps.now } : { embeddings, now: deps.now, signals: deps.signals };
2858
+ return recall(records, queryEmbedding, query, recallDeps);
2859
+ }
2860
+
2861
+ // src/plugin/memoryInject.ts
2862
+ function buildMemoryInjector(deps, policy) {
2863
+ return async (queryText) => {
2864
+ const query = RecallQuerySchema.parse({
2865
+ ownerKey: policy.ownerKey,
2866
+ limit: policy.maxResults,
2867
+ minSimilarity: policy.minSimilarity,
2868
+ includeShared: policy.includeShared
2869
+ });
2870
+ const results = await recallMemory({ index: deps.index, embedder: deps.embedder, now: deps.now() }, queryText, query);
2871
+ return buildMemoryBlock(results, policy.injection);
2872
+ };
2873
+ }
2874
+
2517
2875
  // src/plugin/toolcalls.ts
2518
2876
  function key(sessionID, callID) {
2519
2877
  return `${sessionID}\x00${callID}`;
@@ -2546,6 +2904,414 @@ function createToolcallTracker(deps) {
2546
2904
  };
2547
2905
  }
2548
2906
 
2907
+ // src/local/embeddings.ts
2908
+ function parseEmbeddingsResponse(payload) {
2909
+ if (typeof payload !== "object" || payload === null || !Array.isArray(payload.data)) {
2910
+ throw new Error("Respuesta de /embeddings malformada: falta 'data'");
2911
+ }
2912
+ const data = payload.data;
2913
+ return data.map((entry, index) => {
2914
+ const embedding = typeof entry === "object" && entry !== null ? entry.embedding : undefined;
2915
+ if (!Array.isArray(embedding) || !embedding.every((n) => typeof n === "number" && Number.isFinite(n))) {
2916
+ throw new Error(`Respuesta de /embeddings malformada: vector inválido en índice ${index}`);
2917
+ }
2918
+ return embedding;
2919
+ });
2920
+ }
2921
+ async function embedTexts(request, deps) {
2922
+ if (request.texts.length === 0) {
2923
+ return [];
2924
+ }
2925
+ let response;
2926
+ try {
2927
+ response = await deps.fetch(embeddingsEndpoint(request.baseURL), {
2928
+ method: "POST",
2929
+ headers: {
2930
+ "content-type": "application/json",
2931
+ accept: "application/json"
2932
+ },
2933
+ body: JSON.stringify({
2934
+ model: request.modelID,
2935
+ input: [...request.texts]
2936
+ })
2937
+ });
2938
+ } catch (error) {
2939
+ throw new Error(`Embeddings local falló: ${errorMessage(error)}`);
2940
+ }
2941
+ if (!response.ok) {
2942
+ throw new Error(`Embeddings local falló: HTTP ${response.status} en /embeddings`);
2943
+ }
2944
+ let payload;
2945
+ try {
2946
+ payload = await response.json();
2947
+ } catch (error) {
2948
+ throw new Error(`Embeddings local falló: ${errorMessage(error)}`);
2949
+ }
2950
+ const vectors = parseEmbeddingsResponse(payload);
2951
+ if (vectors.length !== request.texts.length) {
2952
+ throw new Error(`Embeddings local devolvió ${vectors.length} vectores para ${request.texts.length} textos`);
2953
+ }
2954
+ return vectors;
2955
+ }
2956
+ function createLocalEmbedder(request, deps) {
2957
+ return (texts) => embedTexts({ ...request, texts }, deps);
2958
+ }
2959
+
2960
+ // src/storage/index/bunSqlite.ts
2961
+ async function createBunSqliteDatabase(path) {
2962
+ const mod = await import("bun:sqlite");
2963
+ return new mod.Database(path);
2964
+ }
2965
+
2966
+ // src/memory/merge.ts
2967
+ var SEP = "\x00";
2968
+ function factSlotKey(fact) {
2969
+ return [fact.subject, fact.predicate, fact.ownerKey].join(SEP);
2970
+ }
2971
+ function factValueKey(fact) {
2972
+ return [factSlotKey(fact), fact.object].join(SEP);
2973
+ }
2974
+ function preferenceSlotKey(preference) {
2975
+ return [preference.category, preference.ownerKey].join(SEP);
2976
+ }
2977
+ function preferenceValueKey(preference) {
2978
+ return [preferenceSlotKey(preference), preference.preference].join(SEP);
2979
+ }
2980
+ function entitySlotKey(entity) {
2981
+ return [entity.canonicalName, entity.type, entity.ownerKey].join(SEP);
2982
+ }
2983
+ function coalesceFact(existing, incoming) {
2984
+ const window = coalesceWindow(existing, incoming);
2985
+ return {
2986
+ ...existing,
2987
+ validFrom: window.validFrom,
2988
+ validUntil: window.validUntil,
2989
+ confidence: Math.max(existing.confidence, incoming.confidence)
2990
+ };
2991
+ }
2992
+ function applyFact(state, incoming, now) {
2993
+ const incomingSlot = factSlotKey(incoming);
2994
+ const incomingValue = factValueKey(incoming);
2995
+ const superseded = [];
2996
+ const records = [];
2997
+ let reaffirmed = false;
2998
+ for (const fact of state) {
2999
+ if (!isActiveAt(fact, now)) {
3000
+ records.push(fact);
3001
+ continue;
3002
+ }
3003
+ if (factValueKey(fact) === incomingValue) {
3004
+ records.push(coalesceFact(fact, incoming));
3005
+ reaffirmed = true;
3006
+ continue;
3007
+ }
3008
+ if (factSlotKey(fact) === incomingSlot) {
3009
+ const dead = {
3010
+ ...fact,
3011
+ invalidatedAt: now,
3012
+ supersededBy: incoming.id
3013
+ };
3014
+ superseded.push(dead);
3015
+ records.push(dead);
3016
+ continue;
3017
+ }
3018
+ records.push(fact);
3019
+ }
3020
+ if (!reaffirmed) {
3021
+ records.push(incoming);
3022
+ }
3023
+ return { records, superseded };
3024
+ }
3025
+ function applyPreference(state, incoming, now) {
3026
+ const incomingSlot = preferenceSlotKey(incoming);
3027
+ const incomingValue = preferenceValueKey(incoming);
3028
+ const superseded = [];
3029
+ const records = [];
3030
+ let reaffirmed = false;
3031
+ for (const preference of state) {
3032
+ if (!isActiveAt(preference, now)) {
3033
+ records.push(preference);
3034
+ continue;
3035
+ }
3036
+ if (preferenceValueKey(preference) === incomingValue) {
3037
+ records.push({
3038
+ ...preference,
3039
+ confidence: Math.max(preference.confidence, incoming.confidence)
3040
+ });
3041
+ reaffirmed = true;
3042
+ continue;
3043
+ }
3044
+ if (preferenceSlotKey(preference) === incomingSlot) {
3045
+ const dead = {
3046
+ ...preference,
3047
+ invalidatedAt: now,
3048
+ supersededBy: incoming.id
3049
+ };
3050
+ superseded.push(dead);
3051
+ records.push(dead);
3052
+ continue;
3053
+ }
3054
+ records.push(preference);
3055
+ }
3056
+ if (!reaffirmed) {
3057
+ records.push(incoming);
3058
+ }
3059
+ return { records, superseded };
3060
+ }
3061
+ function mergeAliases(existing, incoming) {
3062
+ const seen = new Set(existing);
3063
+ const merged = [...existing];
3064
+ for (const alias of incoming) {
3065
+ if (!seen.has(alias)) {
3066
+ seen.add(alias);
3067
+ merged.push(alias);
3068
+ }
3069
+ }
3070
+ return merged;
3071
+ }
3072
+ function applyEntity(state, incoming, now) {
3073
+ const incomingSlot = entitySlotKey(incoming);
3074
+ const records = [];
3075
+ let merged = false;
3076
+ for (const entity of state) {
3077
+ if (isActiveAt(entity, now) && entitySlotKey(entity) === incomingSlot) {
3078
+ records.push({
3079
+ ...entity,
3080
+ aliases: mergeAliases(entity.aliases, incoming.aliases),
3081
+ confidence: Math.max(entity.confidence, incoming.confidence)
3082
+ });
3083
+ merged = true;
3084
+ continue;
3085
+ }
3086
+ records.push(entity);
3087
+ }
3088
+ if (!merged) {
3089
+ records.push(incoming);
3090
+ }
3091
+ return { records, superseded: [] };
3092
+ }
3093
+
3094
+ // src/memory/fold.ts
3095
+ function emptyState() {
3096
+ return { facts: [], preferences: [], entities: [] };
3097
+ }
3098
+ function applyTombstone(state, tombstone) {
3099
+ const mark = (records) => records.map((record) => record.id === tombstone.id ? {
3100
+ ...record,
3101
+ invalidatedAt: tombstone.invalidatedAt,
3102
+ supersededBy: tombstone.supersededBy
3103
+ } : record);
3104
+ if (tombstone.kind === "fact") {
3105
+ state.facts = mark(state.facts);
3106
+ } else if (tombstone.kind === "preference") {
3107
+ state.preferences = mark(state.preferences);
3108
+ } else {
3109
+ state.entities = mark(state.entities);
3110
+ }
3111
+ }
3112
+ function foldAssertions(assertions) {
3113
+ const state = emptyState();
3114
+ for (const assertion of assertions) {
3115
+ if (assertion.invalidatedAt !== null) {
3116
+ applyTombstone(state, assertion);
3117
+ continue;
3118
+ }
3119
+ if (assertion.kind === "fact") {
3120
+ state.facts = applyFact(state.facts, assertion, assertion.createdAt).records;
3121
+ } else if (assertion.kind === "preference") {
3122
+ state.preferences = applyPreference(state.preferences, assertion, assertion.createdAt).records;
3123
+ } else {
3124
+ state.entities = applyEntity(state.entities, assertion, assertion.createdAt).records;
3125
+ }
3126
+ }
3127
+ return state;
3128
+ }
3129
+ function allRecords(state) {
3130
+ return [...state.facts, ...state.preferences, ...state.entities];
3131
+ }
3132
+
3133
+ // src/memory/log.ts
3134
+ function parseMemoryLog(text) {
3135
+ const records = [];
3136
+ for (const line of text.split(`
3137
+ `)) {
3138
+ const trimmed = line.trim();
3139
+ if (trimmed.length === 0) {
3140
+ continue;
3141
+ }
3142
+ let candidate;
3143
+ try {
3144
+ candidate = JSON.parse(trimmed);
3145
+ } catch {
3146
+ continue;
3147
+ }
3148
+ const parsed = MemoryRecordSchema.safeParse(candidate);
3149
+ if (parsed.success) {
3150
+ records.push(parsed.data);
3151
+ }
3152
+ }
3153
+ return records;
3154
+ }
3155
+
3156
+ // src/storage/index/memoryIndex.ts
3157
+ var SCHEMA = `
3158
+ CREATE TABLE IF NOT EXISTS memory_records (
3159
+ id TEXT PRIMARY KEY,
3160
+ kind TEXT NOT NULL,
3161
+ owner_key TEXT NOT NULL,
3162
+ created_at INTEGER NOT NULL,
3163
+ invalidated_at INTEGER,
3164
+ payload TEXT NOT NULL
3165
+ );
3166
+ CREATE INDEX IF NOT EXISTS idx_memory_owner ON memory_records(owner_key, kind);
3167
+ CREATE TABLE IF NOT EXISTS memory_embeddings (
3168
+ record_id TEXT PRIMARY KEY,
3169
+ dim INTEGER NOT NULL,
3170
+ vector TEXT NOT NULL
3171
+ );
3172
+ `;
3173
+ var INSERT_SQL = `INSERT OR REPLACE INTO memory_records
3174
+ (id, kind, owner_key, created_at, invalidated_at, payload)
3175
+ VALUES (?, ?, ?, ?, ?, ?)`;
3176
+ var INSERT_EMBEDDING_SQL = `INSERT OR REPLACE INTO memory_embeddings
3177
+ (record_id, dim, vector) VALUES (?, ?, ?)`;
3178
+ function insertRecords(db, records) {
3179
+ const stmt = db.query(INSERT_SQL);
3180
+ db.run("BEGIN");
3181
+ try {
3182
+ for (const record of records) {
3183
+ stmt.run(record.id, record.kind, record.ownerKey, record.createdAt, record.invalidatedAt, JSON.stringify(record));
3184
+ }
3185
+ db.run("COMMIT");
3186
+ } catch (error) {
3187
+ db.run("ROLLBACK");
3188
+ throw error;
3189
+ }
3190
+ }
3191
+ function createMemoryIndex(db) {
3192
+ db.exec(SCHEMA);
3193
+ const allRecordsFn = () => {
3194
+ const rows = db.query("SELECT payload FROM memory_records ORDER BY created_at ASC, id ASC").all();
3195
+ const records = [];
3196
+ for (const row of rows) {
3197
+ let candidate;
3198
+ try {
3199
+ candidate = JSON.parse(row.payload);
3200
+ } catch {
3201
+ continue;
3202
+ }
3203
+ const parsed = MemoryRecordSchema.safeParse(candidate);
3204
+ if (parsed.success) {
3205
+ records.push(parsed.data);
3206
+ }
3207
+ }
3208
+ return records;
3209
+ };
3210
+ const state = () => {
3211
+ const facts = [];
3212
+ const preferences = [];
3213
+ const entities = [];
3214
+ for (const record of allRecordsFn()) {
3215
+ if (record.kind === "fact") {
3216
+ facts.push(record);
3217
+ } else if (record.kind === "preference") {
3218
+ preferences.push(record);
3219
+ } else {
3220
+ entities.push(record);
3221
+ }
3222
+ }
3223
+ return { facts, preferences, entities };
3224
+ };
3225
+ return {
3226
+ rebuild(assertions) {
3227
+ const folded = allRecords(foldAssertions(assertions));
3228
+ db.run("DELETE FROM memory_records");
3229
+ insertRecords(db, folded);
3230
+ },
3231
+ allRecords: allRecordsFn,
3232
+ state,
3233
+ activeAt(asOf) {
3234
+ return filterActive(allRecordsFn(), asOf);
3235
+ },
3236
+ putEmbeddings(entries) {
3237
+ const stmt = db.query(INSERT_EMBEDDING_SQL);
3238
+ db.run("BEGIN");
3239
+ try {
3240
+ for (const entry of entries) {
3241
+ stmt.run(entry.recordId, entry.vector.length, JSON.stringify(entry.vector));
3242
+ }
3243
+ db.run("COMMIT");
3244
+ } catch (error) {
3245
+ db.run("ROLLBACK");
3246
+ throw error;
3247
+ }
3248
+ },
3249
+ embeddingMap() {
3250
+ const rows = db.query("SELECT record_id, vector FROM memory_embeddings").all();
3251
+ const map = new Map;
3252
+ for (const row of rows) {
3253
+ try {
3254
+ const vector = JSON.parse(row.vector);
3255
+ if (Array.isArray(vector) && vector.every((n) => typeof n === "number")) {
3256
+ map.set(row.record_id, vector);
3257
+ }
3258
+ } catch {}
3259
+ }
3260
+ return map;
3261
+ },
3262
+ pruneEmbeddings() {
3263
+ const result = db.run("DELETE FROM memory_embeddings WHERE record_id NOT IN (SELECT id FROM memory_records)");
3264
+ return typeof result?.changes === "number" ? result.changes : 0;
3265
+ },
3266
+ close() {
3267
+ db.close();
3268
+ }
3269
+ };
3270
+ }
3271
+ async function rebuildMemoryIndexFromStorage(index, logPath, deps) {
3272
+ const text = await deps.storage.read(logPath) ?? "";
3273
+ const assertions = parseMemoryLog(text);
3274
+ index.rebuild(assertions);
3275
+ return assertions.length;
3276
+ }
3277
+
3278
+ // src/storage/index/memoryRuntime.ts
3279
+ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
3280
+ const semantic = resolveMemorySemantic(config);
3281
+ if (!semantic.enabled || semantic.embeddings.provider === "none") {
3282
+ return;
3283
+ }
3284
+ if (options.requireInjectionEnabled === true && !semantic.injection.enabled) {
3285
+ return;
3286
+ }
3287
+ const runtime = config.local.runtimes.find((rt) => rt.enabled && rt.baseURL !== undefined);
3288
+ if (runtime?.baseURL === undefined) {
3289
+ return;
3290
+ }
3291
+ const modelID = semantic.embeddings.model?.modelID ?? runtime.defaultModel.modelID;
3292
+ const openDatabase = deps.openDatabase ?? createBunSqliteDatabase;
3293
+ const makeEmbedder = deps.createEmbedder ?? createLocalEmbedder;
3294
+ const fetchImpl = deps.fetch ?? globalThis.fetch;
3295
+ const now = deps.now ?? (() => Date.now());
3296
+ try {
3297
+ const db = await openDatabase(semantic.indexPath);
3298
+ const index = createMemoryIndex(db);
3299
+ await rebuildMemoryIndexFromStorage(index, semantic.logPath, {
3300
+ storage: deps.storage
3301
+ });
3302
+ const embedder = makeEmbedder({ baseURL: runtime.baseURL, modelID }, { fetch: fetchImpl });
3303
+ return {
3304
+ index,
3305
+ embedder,
3306
+ now,
3307
+ ownerKey: semantic.scope === "user" ? "user" : "project",
3308
+ semantic
3309
+ };
3310
+ } catch {
3311
+ return;
3312
+ }
3313
+ }
3314
+
2549
3315
  // src/index.ts
2550
3316
  function createShellExec($) {
2551
3317
  return async (command, args) => {
@@ -2565,6 +3331,24 @@ function logTelemetryError(error) {
2565
3331
  const message = error instanceof Error ? error.message : String(error);
2566
3332
  console.warn(`[openteam] telemetry write failed: ${message}`);
2567
3333
  }
3334
+ async function buildMemoryInjectorFromConfig(config, storage) {
3335
+ const runtime = await buildMemoryRuntimeFromConfig(config, { storage }, { requireInjectionEnabled: true });
3336
+ if (runtime === undefined) {
3337
+ return;
3338
+ }
3339
+ const { index, embedder, now, ownerKey, semantic } = runtime;
3340
+ return buildMemoryInjector({ index, embedder, now }, {
3341
+ ownerKey,
3342
+ minSimilarity: semantic.recall.minSimilarity,
3343
+ maxResults: semantic.recall.maxResults,
3344
+ includeShared: true,
3345
+ injection: {
3346
+ maxChars: semantic.injection.maxChars,
3347
+ maxItems: semantic.injection.maxItems,
3348
+ header: "Contexto de memoria de openteam (solo referencia):"
3349
+ }
3350
+ });
3351
+ }
2568
3352
  function telemetryOptions(rawOptions) {
2569
3353
  const telemetry = rawOptions !== null && typeof rawOptions === "object" && "telemetry" in rawOptions && rawOptions.telemetry !== null && typeof rawOptions.telemetry === "object" ? rawOptions.telemetry : {};
2570
3354
  const enabled = !("enabled" in telemetry) || telemetry.enabled !== false;
@@ -2645,7 +3429,8 @@ var server = async (ctx, rawOptions) => {
2645
3429
  const sessionsDir = telemetryOptions(rawOptions).sessionsDir;
2646
3430
  const storage = createFsStorageProvider(process.cwd());
2647
3431
  const sink = createEventSink(rawOptions, storage);
2648
- const hooks = createHooks(config, cache.get, { sink });
3432
+ const injectMemory = await buildMemoryInjectorFromConfig(config, storage);
3433
+ const hooks = createHooks(config, cache.get, injectMemory === undefined ? { sink } : { sink, injectMemory });
2649
3434
  const cliDeps = createCliDeps(config, registry, telemetryPath, sessionsDir, storage);
2650
3435
  const toolcalls = createToolcallTracker({ sink });
2651
3436
  const now = () => Date.now();