@massa-ai/tools-api 1.47.0 → 1.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +211 -51
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -401,7 +401,7 @@ var init_xdg = () => {};
401
401
 
402
402
  // ../../packages/shared/dist/config/massa-ai-config.js
403
403
  import path2 from "path";
404
- var SCHEDULER_JOB_KINDS, defaultMassaAiConfig;
404
+ var SCHEDULER_JOB_KINDS, MAX_MATCH_WORK = 1e5, MAX_IGNORE_PATTERNS = 1024, DEFAULT_CAPTURE_POLICY, DEFAULT_SCHEDULER_CONFIG, defaultMassaAiConfig;
405
405
  var init_massa_ai_config = __esm(() => {
406
406
  init_xdg();
407
407
  SCHEDULER_JOB_KINDS = [
@@ -411,6 +411,54 @@ var init_massa_ai_config = __esm(() => {
411
411
  "observation-bridge",
412
412
  "checkpoint-purge"
413
413
  ];
414
+ DEFAULT_CAPTURE_POLICY = {
415
+ rules: [
416
+ { pattern: "**/node_modules/**", disposition: "Drop" },
417
+ { pattern: "**/.git/**", disposition: "Drop" },
418
+ { pattern: "**/dist/**", disposition: "Drop" },
419
+ { pattern: "**/build/**", disposition: "Drop" },
420
+ { pattern: "**/coverage/**", disposition: "Drop" },
421
+ { pattern: ".env", disposition: "Drop" },
422
+ { pattern: ".env.*", disposition: "Drop" },
423
+ { pattern: "**/generated/**", disposition: "Drop" },
424
+ { pattern: "**/*.generated.*", disposition: "Drop" },
425
+ { pattern: "**/*.d.ts", disposition: "Drop" },
426
+ { pattern: "**/__tests__/**", disposition: "Drop" },
427
+ { pattern: "**/tests/**", disposition: "Drop" },
428
+ { pattern: "**/*.test.ts", disposition: "Drop" },
429
+ { pattern: "**/*.test.tsx", disposition: "Drop" },
430
+ { pattern: "**/*.test.js", disposition: "Drop" },
431
+ { pattern: "**/*.test.jsx", disposition: "Drop" },
432
+ { pattern: "**/*.spec.ts", disposition: "Drop" },
433
+ { pattern: "**/*.spec.tsx", disposition: "Drop" },
434
+ { pattern: "**/*.spec.js", disposition: "Drop" },
435
+ { pattern: "**/*.spec.jsx", disposition: "Drop" },
436
+ { pattern: "**/benchmarks/**", disposition: "Drop" },
437
+ { pattern: "**/fixtures/**", disposition: "Drop" },
438
+ { pattern: "**/*.wasm*", disposition: "Drop" },
439
+ { pattern: "**/*.min.*", disposition: "Drop" },
440
+ { pattern: "**/*.map", disposition: "Drop" },
441
+ { pattern: "**/lock.yaml", disposition: "Drop" },
442
+ { pattern: "**/pnpm-lock.yaml", disposition: "Drop" },
443
+ { pattern: "**/package-lock.json", disposition: "Drop" },
444
+ { pattern: "**/bun.lockb", disposition: "Drop" },
445
+ { pattern: "**/yarn.lock", disposition: "Drop" }
446
+ ],
447
+ maxMatchWork: MAX_MATCH_WORK,
448
+ maxIgnorePatterns: MAX_IGNORE_PATTERNS
449
+ };
450
+ DEFAULT_SCHEDULER_CONFIG = {
451
+ enabled: false,
452
+ tickMs: 60000,
453
+ maxConcurrent: 2,
454
+ jobs: {
455
+ "memory-consolidation": { enabled: false, intervalMs: 30 * 60 * 1000 },
456
+ "decay-sweep": { enabled: false, intervalMs: 60 * 60 * 1000 },
457
+ "auto-improve": { enabled: false, intervalMs: 30 * 60 * 1000 },
458
+ "observation-bridge": { enabled: false, intervalMs: 30 * 60 * 1000 },
459
+ "checkpoint-purge": { enabled: false, intervalMs: 60 * 60 * 1000 }
460
+ }
461
+ };
414
462
  defaultMassaAiConfig = {
415
463
  database: {
416
464
  url: ""
@@ -429,7 +477,7 @@ var init_massa_ai_config = __esm(() => {
429
477
  impact: {
430
478
  bfsCteEnabled: false
431
479
  },
432
- capturePolicy: undefined,
480
+ capturePolicy: DEFAULT_CAPTURE_POLICY,
433
481
  cache: {
434
482
  enabled: true,
435
483
  l1MaxSizeMB: 100,
@@ -552,7 +600,8 @@ var init_massa_ai_config = __esm(() => {
552
600
  },
553
601
  security: {
554
602
  corsOrigins: []
555
- }
603
+ },
604
+ scheduler: DEFAULT_SCHEDULER_CONFIG
556
605
  };
557
606
  });
558
607
 
@@ -562,6 +611,8 @@ __export(exports_config_loader, {
562
611
  writeFileAtomically: () => writeFileAtomically,
563
612
  saveConfig: () => saveConfig,
564
613
  migrateDataDirOnce: () => migrateDataDirOnce,
614
+ mergeSchedulerSection: () => mergeSchedulerSection,
615
+ loadRawUserConfig: () => loadRawUserConfig,
565
616
  loadConfigSafe: () => loadConfigSafe,
566
617
  loadConfig: () => loadConfig,
567
618
  initConfig: () => initConfig,
@@ -584,6 +635,22 @@ function getConfigPath() {
584
635
  function configExists() {
585
636
  return fs.existsSync(CONFIG_FILE);
586
637
  }
638
+ function mergeSchedulerJobs(base, incoming) {
639
+ const defaults = defaultMassaAiConfig.scheduler?.jobs ?? {};
640
+ const merged = {};
641
+ for (const kind of Object.keys(defaults)) {
642
+ merged[kind] = { ...defaults[kind], ...base?.[kind], ...incoming?.[kind] };
643
+ }
644
+ return merged;
645
+ }
646
+ function mergeSchedulerSection(base, incoming) {
647
+ return {
648
+ ...defaultMassaAiConfig.scheduler,
649
+ ...base,
650
+ ...incoming,
651
+ jobs: mergeSchedulerJobs(base?.jobs, incoming?.jobs)
652
+ };
653
+ }
587
654
  function loadConfig() {
588
655
  if (!fs.existsSync(CONFIG_FILE)) {
589
656
  return defaultMassaAiConfig;
@@ -604,13 +671,27 @@ function loadConfig() {
604
671
  memory: { ...defaultMassaAiConfig.memory, ...userConfig.memory },
605
672
  hooks: { ...defaultMassaAiConfig.hooks, ...userConfig.hooks },
606
673
  handoffs: { ...defaultMassaAiConfig.handoffs, ...userConfig.handoffs },
607
- security: { ...defaultMassaAiConfig.security, ...userConfig.security }
674
+ security: { ...defaultMassaAiConfig.security, ...userConfig.security },
675
+ scheduler: mergeSchedulerSection(undefined, userConfig.scheduler),
676
+ capturePolicy: userConfig.capturePolicy ?? defaultMassaAiConfig.capturePolicy
608
677
  };
609
678
  } catch (error) {
610
679
  console.error(`Error loading config from ${CONFIG_FILE}:`, error);
611
680
  return defaultMassaAiConfig;
612
681
  }
613
682
  }
683
+ function loadRawUserConfig() {
684
+ try {
685
+ if (!fs.existsSync(CONFIG_FILE))
686
+ return {};
687
+ const parsed = JSON.parse(fs.readFileSync(CONFIG_FILE, "utf-8"));
688
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
689
+ return {};
690
+ return parsed;
691
+ } catch {
692
+ return {};
693
+ }
694
+ }
614
695
  function loadConfigSafe() {
615
696
  try {
616
697
  return loadConfig();
@@ -1080,6 +1161,9 @@ function savePartialConfig(partial) {
1080
1161
  return { success: false, details };
1081
1162
  }
1082
1163
  const merged = { ...current, ...mergedPartial };
1164
+ if (mergedPartial.scheduler !== undefined) {
1165
+ merged.scheduler = mergeSchedulerSection(current.scheduler, mergedPartial.scheduler);
1166
+ }
1083
1167
  const configPath = getConfigPath();
1084
1168
  if (fs2.existsSync(configPath)) {
1085
1169
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
@@ -1243,6 +1327,31 @@ function parsePositiveIntEnv(raw2, defaultValue, opts) {
1243
1327
  return Number.isInteger(n) && n >= floor ? n : defaultValue;
1244
1328
  }
1245
1329
 
1330
+ // ../../packages/shared/dist/config/embedding-dimensions.js
1331
+ function knownEmbeddingDimensions(model) {
1332
+ if (!model)
1333
+ return;
1334
+ return KNOWN_EMBEDDING_DIMENSIONS[model.trim()];
1335
+ }
1336
+ function resolveEmbeddingDimensions(model, configured, envValue) {
1337
+ if (envValue !== undefined)
1338
+ return { dimensions: envValue };
1339
+ const known = knownEmbeddingDimensions(model);
1340
+ if (known !== undefined) {
1341
+ return configured !== undefined && configured !== known ? { dimensions: known, correctedFrom: configured } : { dimensions: known };
1342
+ }
1343
+ return { dimensions: configured ?? DEFAULT_EMBEDDING_DIMENSIONS };
1344
+ }
1345
+ var KNOWN_EMBEDDING_DIMENSIONS, DEFAULT_EMBEDDING_DIMENSIONS = 2560;
1346
+ var init_embedding_dimensions = __esm(() => {
1347
+ KNOWN_EMBEDDING_DIMENSIONS = {
1348
+ "qwen3-embedding:8b": 4096,
1349
+ "qwen3-embedding:4b": 2560,
1350
+ "qwen3-embedding:0.6b": 1024,
1351
+ "bge-m3": 1024
1352
+ };
1353
+ });
1354
+
1246
1355
  // ../../packages/shared/dist/config/index.js
1247
1356
  import path6 from "path";
1248
1357
  function envNum(key, fallback) {
@@ -1490,16 +1599,18 @@ class Config {
1490
1599
  this.config[key] = value;
1491
1600
  }
1492
1601
  }
1493
- var DEFAULT_LLM_MODEL = "qwen2.5:7b-instruct", DEFAULT_LLM_CODE_MODEL = "qwen2.5-coder:7b", SCHEDULER_JOB_ENV, MAX_IGNORE_PATTERNS = 1024, DEFAULT_ALLOWED_EXTENSIONS, fileConfig, fileCacheL1Bytes, fileCacheL2Bytes, resolvedDataDir, defaultConfig, config;
1602
+ var DEFAULT_LLM_MODEL = "qwen2.5:7b-instruct", DEFAULT_LLM_CODE_MODEL = "qwen2.5-coder:7b", SCHEDULER_JOB_ENV, DEFAULT_ALLOWED_EXTENSIONS, fileConfig, fileCacheL1Bytes, fileCacheL2Bytes, resolvedDataDir, defaultConfig, config;
1494
1603
  var init_config = __esm(() => {
1495
1604
  init_env();
1496
1605
  init_config_loader();
1497
1606
  init_massa_ai_config();
1498
1607
  init_massa_ai_config();
1608
+ init_massa_ai_config();
1499
1609
  init_config_loader();
1500
1610
  init_config_writer();
1501
1611
  init_xdg();
1502
1612
  init_api_key();
1613
+ init_embedding_dimensions();
1503
1614
  SCHEDULER_JOB_ENV = {
1504
1615
  "memory-consolidation": {
1505
1616
  enabledVar: "MASSA_AI_SCHEDULER_CONSOLIDATION_ENABLED",
@@ -14322,7 +14433,7 @@ function validatePolicy(policy, opts = {}) {
14322
14433
  if (!Array.isArray(p.rules))
14323
14434
  throw new TypeError("policy.rules must be an array");
14324
14435
  const dropCount = p.rules.filter((r2) => r2?.disposition === "Drop").length;
14325
- const maxIgnore = typeof p.maxIgnorePatterns === "number" ? p.maxIgnorePatterns : MAX_IGNORE_PATTERNS2;
14436
+ const maxIgnore = typeof p.maxIgnorePatterns === "number" ? p.maxIgnorePatterns : MAX_IGNORE_PATTERNS;
14326
14437
  if (dropCount > maxIgnore) {
14327
14438
  throw new TypeError(`policy: ${dropCount} Drop rules exceed maxIgnorePatterns=${maxIgnore}`);
14328
14439
  }
@@ -14340,7 +14451,7 @@ function matchesGlob2(path16, pattern) {
14340
14451
  }
14341
14452
  return re.test(path16);
14342
14453
  }
14343
- var MAX_MATCH_WORK = 1e5, MAX_IGNORE_PATTERNS2 = 1024, DEFAULT_POLICY, applyPolicy = (filePath, policy) => {
14454
+ var DEFAULT_POLICY, applyPolicy = (filePath, policy) => {
14344
14455
  const normalized = filePath.trim();
14345
14456
  for (const rule of policy.rules) {
14346
14457
  if (matchesGlob2(normalized, rule.pattern))
@@ -14349,42 +14460,8 @@ var MAX_MATCH_WORK = 1e5, MAX_IGNORE_PATTERNS2 = 1024, DEFAULT_POLICY, applyPoli
14349
14460
  return "Keep";
14350
14461
  }, regexCache;
14351
14462
  var init_capture_policy = __esm(() => {
14352
- DEFAULT_POLICY = {
14353
- rules: [
14354
- { pattern: "**/node_modules/**", disposition: "Drop" },
14355
- { pattern: "**/.git/**", disposition: "Drop" },
14356
- { pattern: "**/dist/**", disposition: "Drop" },
14357
- { pattern: "**/build/**", disposition: "Drop" },
14358
- { pattern: "**/coverage/**", disposition: "Drop" },
14359
- { pattern: ".env", disposition: "Drop" },
14360
- { pattern: ".env.*", disposition: "Drop" },
14361
- { pattern: "**/generated/**", disposition: "Drop" },
14362
- { pattern: "**/*.generated.*", disposition: "Drop" },
14363
- { pattern: "**/*.d.ts", disposition: "Drop" },
14364
- { pattern: "**/__tests__/**", disposition: "Drop" },
14365
- { pattern: "**/tests/**", disposition: "Drop" },
14366
- { pattern: "**/*.test.ts", disposition: "Drop" },
14367
- { pattern: "**/*.test.tsx", disposition: "Drop" },
14368
- { pattern: "**/*.test.js", disposition: "Drop" },
14369
- { pattern: "**/*.test.jsx", disposition: "Drop" },
14370
- { pattern: "**/*.spec.ts", disposition: "Drop" },
14371
- { pattern: "**/*.spec.tsx", disposition: "Drop" },
14372
- { pattern: "**/*.spec.js", disposition: "Drop" },
14373
- { pattern: "**/*.spec.jsx", disposition: "Drop" },
14374
- { pattern: "**/benchmarks/**", disposition: "Drop" },
14375
- { pattern: "**/fixtures/**", disposition: "Drop" },
14376
- { pattern: "**/*.wasm*", disposition: "Drop" },
14377
- { pattern: "**/*.min.*", disposition: "Drop" },
14378
- { pattern: "**/*.map", disposition: "Drop" },
14379
- { pattern: "**/lock.yaml", disposition: "Drop" },
14380
- { pattern: "**/pnpm-lock.yaml", disposition: "Drop" },
14381
- { pattern: "**/package-lock.json", disposition: "Drop" },
14382
- { pattern: "**/bun.lockb", disposition: "Drop" },
14383
- { pattern: "**/yarn.lock", disposition: "Drop" }
14384
- ],
14385
- maxMatchWork: MAX_MATCH_WORK,
14386
- maxIgnorePatterns: MAX_IGNORE_PATTERNS2
14387
- };
14463
+ init_dist();
14464
+ DEFAULT_POLICY = DEFAULT_CAPTURE_POLICY;
14388
14465
  regexCache = new Map;
14389
14466
  });
14390
14467
 
@@ -106532,11 +106609,17 @@ var init_config2 = __esm(() => {
106532
106609
  ollama: (() => {
106533
106610
  const file3 = fileFor("ollama");
106534
106611
  const model = process.env.OLLAMA_EMBEDDING_MODEL || file3?.model || "qwen3-embedding:4b";
106612
+ const rawEnvDimensions = Number(process.env.OLLAMA_EMBEDDING_DIMENSIONS);
106613
+ const envDimensions = Number.isInteger(rawEnvDimensions) && rawEnvDimensions > 0 ? rawEnvDimensions : undefined;
106614
+ const resolvedDimensions = resolveEmbeddingDimensions(model, file3?.dimensions, envDimensions);
106615
+ if (resolvedDimensions.correctedFrom !== undefined) {
106616
+ logger.warn(`[ollama] config.json records embedding.dimensions ${resolvedDimensions.correctedFrom} for model ` + `"${model}", which emits ${resolvedDimensions.dimensions}. Using ${resolvedDimensions.dimensions}. ` + "Update embedding.dimensions in config.json (or set OLLAMA_EMBEDDING_DIMENSIONS) to silence this.");
106617
+ }
106535
106618
  return {
106536
106619
  provider: "ollama",
106537
106620
  model,
106538
106621
  baseURL: process.env.OLLAMA_BASE_URL || file3?.baseURL || "http://localhost:11434",
106539
- dimensions: process.env.OLLAMA_EMBEDDING_DIMENSIONS ? Number(process.env.OLLAMA_EMBEDDING_DIMENSIONS) : file3?.dimensions ?? 2560,
106622
+ dimensions: resolvedDimensions.dimensions,
106540
106623
  priority: selectedProvider === "ollama" ? 1 : 50,
106541
106624
  timeout: 300000,
106542
106625
  maxRetries: 2,
@@ -133438,7 +133521,7 @@ function envNum2(key, fileValue, fallback) {
133438
133521
  }
133439
133522
  function readFileSchedulerJobs() {
133440
133523
  try {
133441
- const raw2 = loadConfigSafe().scheduler?.jobs;
133524
+ const raw2 = loadRawUserConfig().scheduler?.jobs;
133442
133525
  return raw2;
133443
133526
  } catch {
133444
133527
  return;
@@ -183086,6 +183169,67 @@ function renderTxtLine(entry2) {
183086
183169
  }
183087
183170
  var SSE_HEARTBEAT_MS_DEFAULT = 15000;
183088
183171
  var SSE_MAX_DURATION_MS_DEFAULT = 10 * 60 * 1000;
183172
+ var SINK_POLL_MS_DEFAULT = 1000;
183173
+ var SINK_POLL_MAX_BYTES = 1024 * 1024;
183174
+ function startSinkTail(enqueue) {
183175
+ const loggingConfig = config.get("logging");
183176
+ const filePath = loggingConfig.file;
183177
+ if (!filePath)
183178
+ return;
183179
+ const initial = activeReader.listFiles(filePath, loggingConfig.maxFiles);
183180
+ if (initial.length === 0)
183181
+ return;
183182
+ let currentFile = initial[0];
183183
+ let offset;
183184
+ try {
183185
+ offset = fs28.statSync(currentFile).size;
183186
+ } catch {
183187
+ return;
183188
+ }
183189
+ let carry = "";
183190
+ let prevTs = new Date().toISOString();
183191
+ let seq = 0;
183192
+ const pollMs = Number(process.env.MASSA_AI_SSE_SINK_POLL_MS) || SINK_POLL_MS_DEFAULT;
183193
+ const timer = setInterval(() => {
183194
+ try {
183195
+ const newest = activeReader.listFiles(filePath, loggingConfig.maxFiles)[0];
183196
+ if (newest && newest !== currentFile) {
183197
+ currentFile = newest;
183198
+ offset = 0;
183199
+ carry = "";
183200
+ }
183201
+ const size = fs28.statSync(currentFile).size;
183202
+ if (size < offset) {
183203
+ offset = 0;
183204
+ carry = "";
183205
+ }
183206
+ if (size === offset)
183207
+ return;
183208
+ const length = Math.min(size - offset, SINK_POLL_MAX_BYTES);
183209
+ const buf = Buffer.alloc(length);
183210
+ const fd = fs28.openSync(currentFile, "r");
183211
+ try {
183212
+ fs28.readSync(fd, buf, 0, length, offset);
183213
+ } finally {
183214
+ fs28.closeSync(fd);
183215
+ }
183216
+ offset += length;
183217
+ const text3 = carry + buf.toString("utf8");
183218
+ const lines = text3.split(`
183219
+ `);
183220
+ carry = lines.pop() ?? "";
183221
+ for (const line of lines) {
183222
+ if (line.trim() === "")
183223
+ continue;
183224
+ const parsed = parseLine(line, prevTs);
183225
+ prevTs = parsed.ts;
183226
+ enqueue({ ...parsed.entry, seq: ++seq });
183227
+ }
183228
+ } catch {}
183229
+ }, pollMs);
183230
+ timer.unref?.();
183231
+ return () => clearInterval(timer);
183232
+ }
183089
183233
  var logsRoutes = new Elysia({ prefix: "/api/v1/logs" }).get("/", ({ query, set: set3 }) => {
183090
183234
  const parsed = parseRangeQuery(query);
183091
183235
  if (isQueryValidationError(parsed)) {
@@ -183151,9 +183295,14 @@ var logsRoutes = new Elysia({ prefix: "/api/v1/logs" }).get("/", ({ query, set:
183151
183295
  closed = true;
183152
183296
  }
183153
183297
  };
183154
- unsubscribe = logBuffer.subscribe((entry2) => {
183155
- enqueue(entry2);
183156
- });
183298
+ const tail = startSinkTail(enqueue);
183299
+ if (tail) {
183300
+ unsubscribe = tail;
183301
+ } else {
183302
+ unsubscribe = logBuffer.subscribe((entry2) => {
183303
+ enqueue(entry2);
183304
+ });
183305
+ }
183157
183306
  heartbeatTimer = setInterval(() => {
183158
183307
  if (closed) {
183159
183308
  clearInterval(heartbeatTimer);
@@ -183331,9 +183480,20 @@ warnIfTrustOverrideEnabled();
183331
183480
  await listenAfterParserValidation({
183332
183481
  validate: validateAllGrammars,
183333
183482
  listen: () => {
183334
- app.listen(PORT, (server) => {
183335
- setServerStopper(() => void server.stop());
183336
- });
183483
+ try {
183484
+ app.listen({ port: Number(PORT), reusePort: false }, (server) => {
183485
+ setServerStopper(() => void server.stop());
183486
+ });
183487
+ } catch (error51) {
183488
+ if (error51?.code === "EADDRINUSE") {
183489
+ console.error(`Port ${PORT} is already in use \u2014 another massa-ai Tools API is running.
183490
+ ` + `Stop it first, or start this one on another port:
183491
+ ` + ` lsof -nP -iTCP:${PORT} -sTCP:LISTEN
183492
+ ` + ` MASSA_AI_API_PORT=<other> bun run dev:api`);
183493
+ process.exit(1);
183494
+ }
183495
+ throw error51;
183496
+ }
183337
183497
  },
183338
183498
  onValidationFailure: (error51) => {
183339
183499
  console.error("Structural parser readiness failed; indexing is unavailable:", error51 instanceof Error ? error51.message : error51);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massa-ai/tools-api",
3
- "version": "1.47.0",
3
+ "version": "1.48.0",
4
4
  "author": "luizgmassa",
5
5
  "description": "massa-ai REST API server - Semantic code search, memory, and context compression",
6
6
  "type": "module",
@@ -21,8 +21,8 @@
21
21
  "test": "bun scripts/run-tests-isolated.ts"
22
22
  },
23
23
  "dependencies": {
24
- "@massa-ai/core": "^1.47.0",
25
- "@massa-ai/shared": "^1.47.0",
24
+ "@massa-ai/core": "^1.48.0",
25
+ "@massa-ai/shared": "^1.48.0",
26
26
  "elysia": "^1.2.25",
27
27
  "@elysiajs/swagger": "^1.2.0",
28
28
  "@elysiajs/cors": "^1.2.0",