@massa-ai/mcp-client 1.17.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -38111,6 +38111,16 @@ var init_index_manager = __esm(() => {
38111
38111
  globAsync = glob;
38112
38112
  });
38113
38113
 
38114
+ // ../../packages/core/dist/services/cache/lru-evict.js
38115
+ function evictOldest(cache, maxRetained) {
38116
+ while (cache.size > maxRetained) {
38117
+ const oldest = cache.keys().next().value;
38118
+ if (oldest === undefined)
38119
+ break;
38120
+ cache.delete(oldest);
38121
+ }
38122
+ }
38123
+
38114
38124
  // ../../packages/core/dist/services/search/file-filter-cache.js
38115
38125
  class FileFilterCache {
38116
38126
  cache = new Map;
@@ -38179,18 +38189,7 @@ class FileFilterCache {
38179
38189
  return parts.join("|");
38180
38190
  }
38181
38191
  evictOldest() {
38182
- let oldestKey = null;
38183
- let oldestTime = Infinity;
38184
- for (const [key, entry] of this.cache.entries()) {
38185
- if (entry.createdAt < oldestTime) {
38186
- oldestTime = entry.createdAt;
38187
- oldestKey = key;
38188
- }
38189
- }
38190
- if (oldestKey) {
38191
- this.cache.delete(oldestKey);
38192
- logger.debug("Evicted oldest filter cache entry", { key: oldestKey });
38193
- }
38192
+ evictOldest(this.cache, this.MAX_CACHE_SIZE);
38194
38193
  }
38195
38194
  invalidateProject(projectId) {
38196
38195
  let removed = 0;
@@ -118540,7 +118539,7 @@ var init_memory_repository_factory = __esm(() => {
118540
118539
  init_memory_repository_pg();
118541
118540
  });
118542
118541
 
118543
- // ../../packages/core/dist/services/graph/graph-store-pg.js
118542
+ // ../../packages/core/dist/services/memory-graph/graph-store-pg.js
118544
118543
  function metadataForEdge(edge) {
118545
118544
  return {
118546
118545
  autoExtracted: edge.autoExtracted ?? false,
@@ -118856,7 +118855,7 @@ var init_graph_store_pg = __esm(() => {
118856
118855
  graphStorePg = GraphStorePg.getInstance();
118857
118856
  });
118858
118857
 
118859
- // ../../packages/core/dist/services/graph/graph-store-factory.js
118858
+ // ../../packages/core/dist/services/memory-graph/graph-store-factory.js
118860
118859
  function getGraphStore() {
118861
118860
  if (cachedStore2)
118862
118861
  return cachedStore2;
@@ -129055,6 +129054,133 @@ var init_pipeline = __esm(() => {
129055
129054
  };
129056
129055
  });
129057
129056
 
129057
+ // ../../packages/core/dist/services/indexing/execute-indexing.js
129058
+ async function executeIndexing(request) {
129059
+ const { jobId, projectId, projectPath, forceReindex, warmCache, warmupQueries, include_tests = false, managedRunLease, warmupCache: warmupCache2 } = request;
129060
+ const startTime = Date.now();
129061
+ try {
129062
+ indexJobTracker.updateStatus(jobId, "running");
129063
+ logger.info("Starting project indexing via ETL Pipeline", {
129064
+ jobId,
129065
+ projectPath,
129066
+ projectId,
129067
+ forceReindex,
129068
+ warmCache,
129069
+ include_tests
129070
+ });
129071
+ const etlResult = await EtlPipeline.getInstance().run({
129072
+ projectId,
129073
+ projectPath,
129074
+ jobId,
129075
+ forceReindex,
129076
+ include_tests,
129077
+ managedRunLease
129078
+ });
129079
+ const duration3 = Date.now() - startTime;
129080
+ logger.info("ETL Pipeline completed", {
129081
+ jobId,
129082
+ projectId,
129083
+ duration: duration3,
129084
+ filesIndexed: etlResult.filesIndexed,
129085
+ filesSkipped: etlResult.filesSkipped,
129086
+ chunksIndexed: etlResult.chunksIndexed,
129087
+ symbolsIndexed: etlResult.symbolsIndexed,
129088
+ errors: etlResult.errors,
129089
+ stageTimings: etlResult.stageTimings
129090
+ });
129091
+ if (warmCache) {
129092
+ logger.info("Starting cache warmup", { jobId, projectId });
129093
+ const warmupStats = await warmupCache2(projectId, projectPath, warmupQueries);
129094
+ logger.info("Cache warmup completed", { jobId, projectId, ...warmupStats });
129095
+ }
129096
+ indexJobTracker.updateProgress(jobId, etlResult.filesIndexed, etlResult.filesIndexed);
129097
+ await indexJobTracker.setResultAndFlush(jobId, {
129098
+ filesIndexed: etlResult.filesIndexed,
129099
+ chunksIndexed: etlResult.chunksIndexed,
129100
+ errors: etlResult.errors,
129101
+ duration: duration3,
129102
+ activatedGraphGenerationId: etlResult.activatedGraphGenerationId,
129103
+ parserDiagnostics: etlResult.parserDiagnostics
129104
+ });
129105
+ } catch (error51) {
129106
+ const duration3 = Date.now() - startTime;
129107
+ logger.error("Project indexing failed", error51, {
129108
+ jobId,
129109
+ projectPath,
129110
+ projectId,
129111
+ duration: duration3
129112
+ });
129113
+ indexJobTracker.setResult(jobId, {
129114
+ filesIndexed: 0,
129115
+ chunksIndexed: 0,
129116
+ errors: 1,
129117
+ duration: duration3
129118
+ }, error51.message);
129119
+ }
129120
+ }
129121
+ var init_execute_indexing = __esm(() => {
129122
+ init_dist();
129123
+ init_index_job_tracker();
129124
+ init_pipeline();
129125
+ });
129126
+
129127
+ // ../../packages/core/dist/services/indexing/acquire-indexing-lease.js
129128
+ async function acquireIndexingLease(request) {
129129
+ const { jobId, projectId } = request;
129130
+ const eventId = `index:${jobId}`;
129131
+ const managedRunRepo = ManagedRunRepositoryPg.getInstance();
129132
+ try {
129133
+ const beginOutcome = await managedRunRepo.begin({
129134
+ projectId,
129135
+ runKind: "indexing",
129136
+ eventId
129137
+ });
129138
+ if (beginOutcome.status === "busy") {
129139
+ indexJobTracker.setResult(jobId, { filesIndexed: 0, chunksIndexed: 0, errors: 0, duration: 0 }, `indexing_busy:${beginOutcome.activeRunId}`);
129140
+ return {
129141
+ status: "busy",
129142
+ activeRunId: beginOutcome.activeRunId,
129143
+ leaseExpiresAt: beginOutcome.leaseExpiresAt
129144
+ };
129145
+ }
129146
+ return { status: "acquired", lease: beginOutcome.lease };
129147
+ } catch (beginError) {
129148
+ logger.error("managed_runs begin failed", beginError, {
129149
+ jobId,
129150
+ projectId
129151
+ });
129152
+ indexJobTracker.setResult(jobId, { filesIndexed: 0, chunksIndexed: 0, errors: 1, duration: 0 }, `managed_runs_begin_failed:${beginError.message}`);
129153
+ return { status: "failed", message: beginError.message };
129154
+ }
129155
+ }
129156
+ var init_acquire_indexing_lease = __esm(() => {
129157
+ init_dist();
129158
+ init_index_job_tracker();
129159
+ init_managed_run_repository_pg();
129160
+ });
129161
+
129162
+ // ../../packages/core/dist/services/project-identity/project-root-identity.js
129163
+ import { realpath as realpath2 } from "fs/promises";
129164
+ import path17 from "path";
129165
+ async function canonicalizeProjectRoot(projectPath, canonicalize = realpath2) {
129166
+ return canonicalize(path17.resolve(projectPath));
129167
+ }
129168
+ async function assertProjectRootReuse(options) {
129169
+ if (!options.storedProjectPath || options.forceReindex)
129170
+ return;
129171
+ const canonicalize = options.canonicalize ?? realpath2;
129172
+ let storedCanonical;
129173
+ try {
129174
+ storedCanonical = await canonicalize(path17.resolve(options.storedProjectPath));
129175
+ } catch {
129176
+ storedCanonical = path17.resolve(options.storedProjectPath);
129177
+ }
129178
+ if (storedCanonical !== options.canonicalProjectPath) {
129179
+ throw new Error(`Project ID "${options.projectId}" already indexes canonical root ` + `"${storedCanonical}", not "${options.canonicalProjectPath}"; ` + "use forceReindex only after verifying ownership of the existing project");
129180
+ }
129181
+ }
129182
+ var init_project_root_identity = () => {};
129183
+
129058
129184
  // ../../packages/core/dist/services/symbol/definition-lookup.js
129059
129185
  function toSymbolIdentityResolution(result) {
129060
129186
  switch (result.status) {
@@ -129790,16 +129916,16 @@ function detectRoutes(httpEdges, defs, opts = {}) {
129790
129916
  const seen = new Set;
129791
129917
  const out = [];
129792
129918
  for (const e of httpEdges) {
129793
- const path17 = e.route;
129794
- if (!path17)
129919
+ const path18 = e.route;
129920
+ if (!path18)
129795
129921
  continue;
129796
129922
  const method = (e.method ?? "ANY").toUpperCase();
129797
- const key = method + " " + path17;
129923
+ const key = method + " " + path18;
129798
129924
  if (seen.has(key))
129799
129925
  continue;
129800
129926
  seen.add(key);
129801
129927
  out.push({
129802
- path: path17,
129928
+ path: path18,
129803
129929
  method: e.method,
129804
129930
  file: e.fromFile,
129805
129931
  handler: e.targetFqn ?? e.symbolName
@@ -129810,12 +129936,12 @@ function detectRoutes(httpEdges, defs, opts = {}) {
129810
129936
  continue;
129811
129937
  const parsed = parseRouteName(d.name);
129812
129938
  const method = parsed?.method ?? "ANY";
129813
- const path17 = parsed?.path ?? d.name;
129814
- const key = method + " " + path17;
129939
+ const path18 = parsed?.path ?? d.name;
129940
+ const key = method + " " + path18;
129815
129941
  if (seen.has(key))
129816
129942
  continue;
129817
129943
  seen.add(key);
129818
- out.push({ path: path17, method: parsed?.method, file: d.filePath, handler: d.name });
129944
+ out.push({ path: path18, method: parsed?.method, file: d.filePath, handler: d.name });
129819
129945
  }
129820
129946
  for (const d of defs) {
129821
129947
  const parsed = parseRouteName(d.name);
@@ -130036,7 +130162,7 @@ __export(exports_symbol_graph_service, {
130036
130162
  symbolGraphService: () => symbolGraphService,
130037
130163
  SymbolGraphService: () => SymbolGraphService
130038
130164
  });
130039
- import path17 from "path";
130165
+ import path18 from "path";
130040
130166
  import fs8 from "fs/promises";
130041
130167
 
130042
130168
  class SymbolGraphService {
@@ -130390,7 +130516,7 @@ class SymbolGraphService {
130390
130516
  }
130391
130517
  async resolveToAbsolute(relativePath, projectId) {
130392
130518
  const root = await this.getProjectRoot(projectId);
130393
- return root ? path17.resolve(root, relativePath) : relativePath;
130519
+ return root ? path18.resolve(root, relativePath) : relativePath;
130394
130520
  }
130395
130521
  async getProjectRoot(projectId) {
130396
130522
  const cached2 = this.projectRootCache.get(projectId);
@@ -130410,12 +130536,7 @@ class SymbolGraphService {
130410
130536
  return null;
130411
130537
  }
130412
130538
  evictOldestProjectRoot() {
130413
- while (this.projectRootCache.size >= this.PROJECT_ROOT_CACHE_MAX_ENTRIES) {
130414
- const oldest = this.projectRootCache.keys().next().value;
130415
- if (oldest === undefined)
130416
- break;
130417
- this.projectRootCache.delete(oldest);
130418
- }
130539
+ evictOldest(this.projectRootCache, this.PROJECT_ROOT_CACHE_MAX_ENTRIES - 1);
130419
130540
  }
130420
130541
  clearProjectRoot(projectId) {
130421
130542
  this.projectRootCache.delete(projectId);
@@ -130538,25 +130659,7 @@ var init_workspace_manager = __esm(() => {
130538
130659
  });
130539
130660
 
130540
130661
  // ../../packages/core/dist/tools/index_project.js
130541
- import { realpath as realpath2 } from "fs/promises";
130542
- import path18 from "path";
130543
- async function canonicalizeProjectRoot(projectPath, canonicalize = realpath2) {
130544
- return canonicalize(path18.resolve(projectPath));
130545
- }
130546
- async function assertProjectRootReuse(options) {
130547
- if (!options.storedProjectPath || options.forceReindex)
130548
- return;
130549
- const canonicalize = options.canonicalize ?? realpath2;
130550
- let storedCanonical;
130551
- try {
130552
- storedCanonical = await canonicalize(path18.resolve(options.storedProjectPath));
130553
- } catch {
130554
- storedCanonical = path18.resolve(options.storedProjectPath);
130555
- }
130556
- if (storedCanonical !== options.canonicalProjectPath) {
130557
- throw new Error(`Project ID "${options.projectId}" already indexes canonical root ` + `"${storedCanonical}", not "${options.canonicalProjectPath}"; ` + "use forceReindex only after verifying ownership of the existing project");
130558
- }
130559
- }
130662
+ import path19 from "path";
130560
130663
 
130561
130664
  class IndexProjectTool {
130562
130665
  name = "index_project";
@@ -130604,7 +130707,7 @@ class IndexProjectTool {
130604
130707
  try {
130605
130708
  await assertParserReadyForIndexing();
130606
130709
  const canonicalProjectPath = await canonicalizeProjectRoot(projectPath);
130607
- const finalProjectId = projectId || path18.basename(canonicalProjectPath) || "default";
130710
+ const finalProjectId = projectId || path19.basename(canonicalProjectPath) || "default";
130608
130711
  const existing = await workspaceManager.getWorkspace(finalProjectId);
130609
130712
  await assertProjectRootReuse({
130610
130713
  projectId: finalProjectId,
@@ -130618,40 +130721,42 @@ class IndexProjectTool {
130618
130721
  projectPath: canonicalProjectPath,
130619
130722
  projectId: finalProjectId
130620
130723
  });
130621
- const eventId = `index:${job.jobId}`;
130622
- const managedRunRepo = ManagedRunRepositoryPg.getInstance();
130623
- let lease;
130624
- try {
130625
- const beginOutcome = await managedRunRepo.begin({
130626
- projectId: finalProjectId,
130627
- runKind: "indexing",
130628
- eventId
130629
- });
130630
- if (beginOutcome.status === "busy") {
130631
- indexJobTracker.setResult(job.jobId, { filesIndexed: 0, chunksIndexed: 0, errors: 0, duration: 0 }, `indexing_busy:${beginOutcome.activeRunId}`);
130632
- return {
130633
- success: false,
130634
- error: `indexing_busy:${beginOutcome.activeRunId}`,
130635
- data: {
130636
- jobId: job.jobId,
130637
- projectId: finalProjectId,
130638
- status: "busy",
130639
- activeRunId: beginOutcome.activeRunId,
130640
- leaseExpiresAt: beginOutcome.leaseExpiresAt,
130641
- message: "Another indexing run is active for this project. Poll get_index_status(activeRunId)."
130642
- }
130643
- };
130644
- }
130645
- lease = beginOutcome.lease;
130646
- } catch (beginError) {
130647
- logger.error("managed_runs begin failed", beginError, { jobId: job.jobId, projectId: finalProjectId });
130648
- indexJobTracker.setResult(job.jobId, { filesIndexed: 0, chunksIndexed: 0, errors: 1, duration: 0 }, `managed_runs_begin_failed:${beginError.message}`);
130724
+ const leaseOutcome = await acquireIndexingLease({
130725
+ jobId: job.jobId,
130726
+ projectId: finalProjectId
130727
+ });
130728
+ if (leaseOutcome.status === "busy") {
130729
+ return {
130730
+ success: false,
130731
+ error: `indexing_busy:${leaseOutcome.activeRunId}`,
130732
+ data: {
130733
+ jobId: job.jobId,
130734
+ projectId: finalProjectId,
130735
+ status: "busy",
130736
+ activeRunId: leaseOutcome.activeRunId,
130737
+ leaseExpiresAt: leaseOutcome.leaseExpiresAt,
130738
+ message: "Another indexing run is active for this project. Poll get_index_status(activeRunId)."
130739
+ }
130740
+ };
130741
+ }
130742
+ if (leaseOutcome.status === "failed") {
130649
130743
  return {
130650
130744
  success: false,
130651
- error: `Failed to acquire indexing lease: ${beginError.message}`
130745
+ error: `Failed to acquire indexing lease: ${leaseOutcome.message}`
130652
130746
  };
130653
130747
  }
130654
- this.executeIndexing(job.jobId, finalProjectId, canonicalProjectPath, forceReindex, warmCache, warmupQueries, include_tests, lease).catch((error51) => {
130748
+ const lease = leaseOutcome.lease;
130749
+ executeIndexing({
130750
+ jobId: job.jobId,
130751
+ projectId: finalProjectId,
130752
+ projectPath: canonicalProjectPath,
130753
+ forceReindex,
130754
+ warmCache,
130755
+ warmupQueries,
130756
+ include_tests,
130757
+ managedRunLease: lease,
130758
+ warmupCache: this.contextualSearch.warmupCache.bind(this.contextualSearch)
130759
+ }).catch((error51) => {
130655
130760
  logger.error("Background indexing failed", error51, {
130656
130761
  jobId: job.jobId
130657
130762
  });
@@ -130678,77 +130783,16 @@ class IndexProjectTool {
130678
130783
  };
130679
130784
  }
130680
130785
  }
130681
- async executeIndexing(jobId, projectId, projectPath, forceReindex, warmCache, warmupQueries, include_tests = false, managedRunLease) {
130682
- const startTime = Date.now();
130683
- try {
130684
- indexJobTracker.updateStatus(jobId, "running");
130685
- logger.info("Starting project indexing via ETL Pipeline", {
130686
- jobId,
130687
- projectPath,
130688
- projectId,
130689
- forceReindex,
130690
- warmCache,
130691
- include_tests
130692
- });
130693
- const etlResult = await EtlPipeline.getInstance().run({
130694
- projectId,
130695
- projectPath,
130696
- jobId,
130697
- forceReindex,
130698
- include_tests,
130699
- managedRunLease
130700
- });
130701
- const duration3 = Date.now() - startTime;
130702
- logger.info("ETL Pipeline completed", {
130703
- jobId,
130704
- projectId,
130705
- duration: duration3,
130706
- filesIndexed: etlResult.filesIndexed,
130707
- filesSkipped: etlResult.filesSkipped,
130708
- chunksIndexed: etlResult.chunksIndexed,
130709
- symbolsIndexed: etlResult.symbolsIndexed,
130710
- errors: etlResult.errors,
130711
- stageTimings: etlResult.stageTimings
130712
- });
130713
- if (warmCache) {
130714
- logger.info("Starting cache warmup", { jobId, projectId });
130715
- const warmupStats = await this.contextualSearch.warmupCache(projectId, projectPath, warmupQueries);
130716
- logger.info("Cache warmup completed", { jobId, projectId, ...warmupStats });
130717
- }
130718
- indexJobTracker.updateProgress(jobId, etlResult.filesIndexed, etlResult.filesIndexed);
130719
- await indexJobTracker.setResultAndFlush(jobId, {
130720
- filesIndexed: etlResult.filesIndexed,
130721
- chunksIndexed: etlResult.chunksIndexed,
130722
- errors: etlResult.errors,
130723
- duration: duration3,
130724
- activatedGraphGenerationId: etlResult.activatedGraphGenerationId,
130725
- parserDiagnostics: etlResult.parserDiagnostics
130726
- });
130727
- } catch (error51) {
130728
- const duration3 = Date.now() - startTime;
130729
- logger.error("Project indexing failed", error51, {
130730
- jobId,
130731
- projectPath,
130732
- projectId,
130733
- duration: duration3
130734
- });
130735
- indexJobTracker.setResult(jobId, {
130736
- filesIndexed: 0,
130737
- chunksIndexed: 0,
130738
- errors: 1,
130739
- duration: duration3
130740
- }, error51.message);
130741
- }
130742
- }
130743
130786
  }
130744
130787
  var init_index_project = __esm(() => {
130745
130788
  init_contextual_search_rlm();
130746
130789
  init_dist();
130747
130790
  init_index_job_tracker();
130748
- init_pipeline();
130791
+ init_execute_indexing();
130792
+ init_acquire_indexing_lease();
130793
+ init_project_root_identity();
130749
130794
  init_workspace_manager();
130750
130795
  init_parser_readiness();
130751
- init_managed_run_repository_pg();
130752
130796
  });
130753
130797
 
130754
130798
  // ../../packages/core/dist/tools/get_index_status.js
@@ -131216,17 +131260,17 @@ function applyReplacer(root, replacer) {
131216
131260
  return transformChildren(root, replacer, []);
131217
131261
  return transformChildren(normalizeValue(replacedRoot), replacer, []);
131218
131262
  }
131219
- function transformChildren(value, replacer, path19) {
131263
+ function transformChildren(value, replacer, path20) {
131220
131264
  if (isJsonObject(value))
131221
- return transformObject(value, replacer, path19);
131265
+ return transformObject(value, replacer, path20);
131222
131266
  if (isJsonArray(value))
131223
- return transformArray(value, replacer, path19);
131267
+ return transformArray(value, replacer, path20);
131224
131268
  return value;
131225
131269
  }
131226
- function transformObject(obj, replacer, path19) {
131270
+ function transformObject(obj, replacer, path20) {
131227
131271
  const result = {};
131228
131272
  for (const [key, value] of Object.entries(obj)) {
131229
- const childPath = [...path19, key];
131273
+ const childPath = [...path20, key];
131230
131274
  const replacedValue = replacer(key, value, childPath);
131231
131275
  if (replacedValue === undefined)
131232
131276
  continue;
@@ -131234,11 +131278,11 @@ function transformObject(obj, replacer, path19) {
131234
131278
  }
131235
131279
  return result;
131236
131280
  }
131237
- function transformArray(arr, replacer, path19) {
131281
+ function transformArray(arr, replacer, path20) {
131238
131282
  const result = [];
131239
131283
  for (let i = 0;i < arr.length; i++) {
131240
131284
  const value = arr[i];
131241
- const childPath = [...path19, i];
131285
+ const childPath = [...path20, i];
131242
131286
  const replacedValue = replacer(String(i), value, childPath);
131243
131287
  if (replacedValue === undefined)
131244
131288
  continue;
@@ -131961,7 +132005,7 @@ var init_memory_service = __esm(() => {
131961
132005
  init_decay();
131962
132006
  });
131963
132007
 
131964
- // ../../packages/core/dist/services/graph/relation-extractor.js
132008
+ // ../../packages/core/dist/services/memory-graph/relation-extractor.js
131965
132009
  class RelationExtractor {
131966
132010
  graphStore;
131967
132011
  constructor(graphStore) {
@@ -132199,7 +132243,7 @@ var init_relation_extractor = __esm(() => {
132199
132243
  ];
132200
132244
  });
132201
132245
 
132202
- // ../../packages/core/dist/services/graph/graph-queries.js
132246
+ // ../../packages/core/dist/services/memory-graph/graph-queries.js
132203
132247
  class GraphQueries {
132204
132248
  graphStore;
132205
132249
  constructor(graphStore) {
@@ -132419,7 +132463,7 @@ var init_graph_queries = __esm(() => {
132419
132463
  };
132420
132464
  });
132421
132465
 
132422
- // ../../packages/core/dist/services/graph/memory-graph.service.js
132466
+ // ../../packages/core/dist/services/memory-graph/memory-graph.service.js
132423
132467
  class MemoryGraphService {
132424
132468
  static instance = null;
132425
132469
  store;
@@ -136306,7 +136350,7 @@ var init_session_pin_store = __esm(() => {
136306
136350
  // ../../packages/core/dist/services/hooks/attribution-resolver.js
136307
136351
  import fs9 from "fs";
136308
136352
  import os3 from "os";
136309
- import path19 from "path";
136353
+ import path20 from "path";
136310
136354
 
136311
136355
  class PgWorkspaceRootProvider {
136312
136356
  cache = null;
@@ -136358,7 +136402,7 @@ class AttributionResolver {
136358
136402
  this.pins = options.pins ?? new SessionPinStore;
136359
136403
  this.canonicalize = options.canonicalize ?? defaultCanonicalize;
136360
136404
  this.homedir = options.homedir ?? os3.homedir;
136361
- this.fsRoot = options.fsRoot ?? (() => path19.parse(path19.sep).root);
136405
+ this.fsRoot = options.fsRoot ?? (() => path20.parse(path20.sep).root);
136362
136406
  }
136363
136407
  async resolve(input) {
136364
136408
  const caller = input.callerProjectId;
@@ -136411,7 +136455,7 @@ class AttributionResolver {
136411
136455
  }
136412
136456
  let bestPath = null;
136413
136457
  for (const candidate2 of byPath.keys()) {
136414
- if (canonicalCwd === candidate2 || canonicalCwd.startsWith(candidate2.endsWith(path19.sep) ? candidate2 : candidate2 + path19.sep)) {
136458
+ if (canonicalCwd === candidate2 || canonicalCwd.startsWith(candidate2.endsWith(path20.sep) ? candidate2 : candidate2 + path20.sep)) {
136415
136459
  if (bestPath === null || candidate2.length > bestPath.length) {
136416
136460
  bestPath = candidate2;
136417
136461
  }
@@ -136434,7 +136478,7 @@ class AttributionResolver {
136434
136478
  return projectPath2;
136435
136479
  const fsRoot = this.fsRoot();
136436
136480
  let normalized = projectPath2;
136437
- while (normalized.length > fsRoot.length && normalized.endsWith(path19.sep)) {
136481
+ while (normalized.length > fsRoot.length && normalized.endsWith(path20.sep)) {
136438
136482
  normalized = normalized.slice(0, -1);
136439
136483
  }
136440
136484
  return normalized;
@@ -136445,7 +136489,7 @@ function defaultCanonicalize(cwd) {
136445
136489
  return fs9.realpathSync(cwd);
136446
136490
  } catch {
136447
136491
  try {
136448
- return path19.resolve(cwd);
136492
+ return path20.resolve(cwd);
136449
136493
  } catch {
136450
136494
  return;
136451
136495
  }
@@ -136464,6 +136508,72 @@ var init_attribution_resolver = __esm(() => {
136464
136508
  init_session_pin_store();
136465
136509
  });
136466
136510
 
136511
+ // ../../packages/core/dist/kernel/sanitize/credential-scrub.js
136512
+ function markerFor(id) {
136513
+ return `[REDACTED:${id}]`;
136514
+ }
136515
+ function fullMatchRule(id, pattern) {
136516
+ const marker26 = markerFor(id);
136517
+ return {
136518
+ id,
136519
+ replace(text3) {
136520
+ let count = 0;
136521
+ const replaced = text3.replace(pattern, () => {
136522
+ count++;
136523
+ return marker26;
136524
+ });
136525
+ return { text: replaced, count };
136526
+ }
136527
+ };
136528
+ }
136529
+ function scrubCredentials(payloadJson) {
136530
+ const redactions = {};
136531
+ for (const id of RULE_IDS)
136532
+ redactions[id] = 0;
136533
+ let text3 = payloadJson;
136534
+ for (const rule of RULES) {
136535
+ const { text: next, count } = rule.replace(text3);
136536
+ text3 = next;
136537
+ redactions[rule.id] += count;
136538
+ }
136539
+ const total = Object.values(redactions).reduce((sum, n) => sum + n, 0);
136540
+ return {
136541
+ sanitized: text3,
136542
+ redactions,
136543
+ total
136544
+ };
136545
+ }
136546
+ var PEM_PATTERN, JWT_PATTERN, AWS_KEY_PATTERN, SK_KEY_PATTERN, GITHUB_TOKEN_PATTERN, SLACK_TOKEN_PATTERN, BEARER_PATTERN, RULES, RULE_IDS;
136547
+ var init_credential_scrub = __esm(() => {
136548
+ PEM_PATTERN = /-----BEGIN [A-Z ]{0,32}PRIVATE KEY-----[\s\S]{0,8192}?-----END [A-Z ]{0,32}PRIVATE KEY-----/g;
136549
+ JWT_PATTERN = /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g;
136550
+ AWS_KEY_PATTERN = /\b(?:AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}\b/g;
136551
+ SK_KEY_PATTERN = /\bsk-[A-Za-z0-9_-]{20,}\b/g;
136552
+ GITHUB_TOKEN_PATTERN = /\bgh[pousr]_[A-Za-z0-9]{36,}\b|\bgithub_pat_[A-Za-z0-9_]{22,}\b/g;
136553
+ SLACK_TOKEN_PATTERN = /\bxox[baprs]-[A-Za-z0-9-]{18,}\b/g;
136554
+ BEARER_PATTERN = /(Bearer\s+)([A-Za-z0-9._~+/=-]{20,})/g;
136555
+ RULES = [
136556
+ fullMatchRule("pem", PEM_PATTERN),
136557
+ fullMatchRule("jwt", JWT_PATTERN),
136558
+ fullMatchRule("aws-key", AWS_KEY_PATTERN),
136559
+ fullMatchRule("sk-key", SK_KEY_PATTERN),
136560
+ fullMatchRule("github-token", GITHUB_TOKEN_PATTERN),
136561
+ fullMatchRule("slack-token", SLACK_TOKEN_PATTERN),
136562
+ {
136563
+ id: "bearer",
136564
+ replace(text3) {
136565
+ let count = 0;
136566
+ const replaced = text3.replace(BEARER_PATTERN, (_m, prefix) => {
136567
+ count++;
136568
+ return `${prefix}${markerFor("bearer")}`;
136569
+ });
136570
+ return { text: replaced, count };
136571
+ }
136572
+ }
136573
+ ];
136574
+ RULE_IDS = RULES.map((r) => r.id);
136575
+ });
136576
+
136467
136577
  // ../../packages/core/dist/tools/compact_snapshot.js
136468
136578
  class CompactSnapshotTool {
136469
136579
  name = "compact_snapshot";
@@ -136515,19 +136625,26 @@ class CompactSnapshotTool {
136515
136625
  persistedId = newObservationId();
136516
136626
  try {
136517
136627
  const attribution = await (this.resolverOverride ?? getAttributionResolver()).resolve({ callerProjectId: projectId, sessionId, cwd });
136628
+ const scrub = scrubCredentials(JSON.stringify({
136629
+ snapshot: snapshot.xml,
136630
+ eventCount: snapshot.eventCount,
136631
+ compactCount: snapshot.compactCount,
136632
+ generatedAt: snapshot.generatedAt,
136633
+ sectionCount: snapshot.sections.length
136634
+ }));
136635
+ if (scrub.total > 0) {
136636
+ logger.debug("compact_snapshot payload redacted before persist", {
136637
+ redactions: scrub.redactions,
136638
+ total: scrub.total
136639
+ });
136640
+ }
136518
136641
  store4.insert({
136519
136642
  id: persistedId,
136520
136643
  projectId: attribution.projectId,
136521
136644
  sessionId,
136522
136645
  source: "pre-compact",
136523
136646
  category: "compaction-snapshots",
136524
- payloadJson: JSON.stringify({
136525
- snapshot: snapshot.xml,
136526
- eventCount: snapshot.eventCount,
136527
- compactCount: snapshot.compactCount,
136528
- generatedAt: snapshot.generatedAt,
136529
- sectionCount: snapshot.sections.length
136530
- }),
136647
+ payloadJson: scrub.sanitized,
136531
136648
  importance: 0.8,
136532
136649
  createdAt: Date.now(),
136533
136650
  attributionSource: attribution.source
@@ -136579,6 +136696,7 @@ var init_compact_snapshot = __esm(() => {
136579
136696
  init_observation_repository();
136580
136697
  init_compaction_snapshot_service();
136581
136698
  init_attribution_resolver();
136699
+ init_credential_scrub();
136582
136700
  });
136583
136701
 
136584
136702
  // ../../packages/core/dist/tools/list_projects.js
@@ -137184,31 +137302,31 @@ class TracePathService {
137184
137302
  const chains = [];
137185
137303
  const seen = new Set;
137186
137304
  let walks = 0;
137187
- const walk = (fqn, path20) => {
137305
+ const walk = (fqn, path21) => {
137188
137306
  if (chains.length >= CHAIN_CAP)
137189
137307
  return;
137190
137308
  if (walks >= MAX_WALKS)
137191
137309
  return;
137192
137310
  walks++;
137193
- const key = path20.join("\u2192");
137311
+ const key = path21.join("\u2192");
137194
137312
  if (seen.has(key))
137195
137313
  return;
137196
137314
  seen.add(key);
137197
137315
  const next = adj.get(fqn);
137198
137316
  if (!next || next.length === 0 || !whoHasChild.has(fqn)) {
137199
- if (path20.length > 1)
137200
- chains.push(path20.map((n) => this.fqnToName(n)).join(" \u2192 "));
137317
+ if (path21.length > 1)
137318
+ chains.push(path21.map((n) => this.fqnToName(n)).join(" \u2192 "));
137201
137319
  return;
137202
137320
  }
137203
137321
  for (const child of next) {
137204
137322
  if (chains.length >= CHAIN_CAP || walks >= MAX_WALKS)
137205
137323
  return;
137206
- if (path20.includes(child)) {
137207
- const cycled = [...path20, `${this.fqnToName(child)}\u21BA`];
137324
+ if (path21.includes(child)) {
137325
+ const cycled = [...path21, `${this.fqnToName(child)}\u21BA`];
137208
137326
  chains.push(cycled.map((n) => n).join(" \u2192 "));
137209
137327
  continue;
137210
137328
  }
137211
- walk(child, [...path20, child]);
137329
+ walk(child, [...path21, child]);
137212
137330
  }
137213
137331
  };
137214
137332
  for (const seed of seeds) {
@@ -138040,216 +138158,231 @@ var init_get_architecture = __esm(() => {
138040
138158
  init_architecture();
138041
138159
  });
138042
138160
 
138043
- // ../../packages/core/dist/tools/read_file.js
138161
+ // ../../packages/core/dist/services/file-read/file-content-cache.js
138044
138162
  import fs10 from "fs/promises";
138045
- import path20 from "path";
138046
138163
 
138047
- class ReadFileTool {
138048
- name = "read_file";
138049
- description = "Read file with automatic compression, caching, and symbol metadata. " + "Use with search results for 60% token savings.";
138050
- inputSchema = {
138051
- type: "object",
138052
- properties: {
138053
- filePath: {
138054
- type: "string",
138055
- description: "File path (absolute or relative to project root)"
138056
- },
138057
- projectId: {
138058
- type: "string",
138059
- description: "Project ID for symbol metadata (optional)"
138060
- },
138061
- offset: {
138062
- type: "number",
138063
- description: "Start line number (1-indexed)"
138064
- },
138065
- limit: {
138066
- type: "number",
138067
- description: "Number of lines to read"
138068
- },
138069
- lineStart: {
138070
- type: "number",
138071
- description: "Start line (alternative to offset)"
138072
- },
138073
- lineEnd: {
138074
- type: "number",
138075
- description: "End line (alternative to limit)"
138076
- },
138077
- compress: {
138078
- type: "boolean",
138079
- description: "Auto-compress content > 100 lines (default: true)",
138080
- default: true
138081
- },
138082
- targetRatio: {
138083
- type: "number",
138084
- description: "Compression target ratio (0.3 = 70% reduction)",
138085
- default: 0.3
138086
- },
138087
- format: {
138088
- type: "string",
138089
- enum: ["json", "toon"],
138090
- description: "Output format",
138091
- default: "json"
138092
- },
138093
- fields: {
138094
- type: "array",
138095
- items: { type: "string" },
138096
- description: "Projection \u2014 keep only these keys (dotted paths supported, e.g. ['nodes.symbol']). Absent/empty \u2192 full data."
138097
- },
138098
- includeSymbols: {
138099
- type: "boolean",
138100
- description: "Include symbol metadata from graph (default: true)",
138101
- default: true
138102
- },
138103
- includeImports: {
138104
- type: "boolean",
138105
- description: "Extract and show import statements (default: true)",
138106
- default: true
138107
- }
138108
- },
138109
- required: ["filePath"]
138110
- };
138111
- compressor;
138112
- symbolGraph;
138164
+ class FileContentCache {
138165
+ extractMetadata;
138113
138166
  fileCache = new Map;
138114
- projectRootCache = new Map;
138115
138167
  CACHE_TTL = 60000;
138116
- ROOT_CACHE_TTL = 300000;
138117
138168
  FILE_CACHE_MAX_ENTRIES = 512;
138169
+ constructor(extractMetadata) {
138170
+ this.extractMetadata = extractMetadata;
138171
+ }
138172
+ async readFileWithCache(filePath, options) {
138173
+ const cacheKey = JSON.stringify({
138174
+ filePath,
138175
+ includeSymbols: options.includeSymbols ?? false,
138176
+ includeImports: options.includeImports ?? false,
138177
+ projectId: options.projectId ?? null,
138178
+ relativePath: options.relativePath ?? null
138179
+ });
138180
+ const cached2 = this.fileCache.get(cacheKey);
138181
+ if (cached2 && Date.now() - cached2.timestamp < this.CACHE_TTL) {
138182
+ logger.debug("File cache hit", { filePath });
138183
+ this.fileCache.delete(cacheKey);
138184
+ this.fileCache.set(cacheKey, cached2);
138185
+ if (!cached2.metadata) {
138186
+ const metadata2 = await this.extractMetadata(cached2.content, filePath, options);
138187
+ this.fileCache.set(cacheKey, { ...cached2, metadata: metadata2 });
138188
+ return { content: cached2.content, metadata: metadata2 };
138189
+ }
138190
+ return {
138191
+ content: cached2.content,
138192
+ metadata: cached2.metadata
138193
+ };
138194
+ }
138195
+ const content = await fs10.readFile(filePath, "utf-8");
138196
+ const metadata = await this.extractMetadata(content, filePath, options);
138197
+ evictOldest(this.fileCache, this.FILE_CACHE_MAX_ENTRIES - 1);
138198
+ this.fileCache.set(cacheKey, {
138199
+ content,
138200
+ timestamp: Date.now(),
138201
+ metadata
138202
+ });
138203
+ logger.debug("File read and cached", { filePath });
138204
+ return { content, metadata };
138205
+ }
138206
+ }
138207
+ var init_file_content_cache = __esm(() => {
138208
+ init_dist();
138209
+ });
138210
+
138211
+ // ../../packages/core/dist/services/file-read/file-metadata.js
138212
+ import path21 from "path";
138213
+
138214
+ class FileMetadataExtractor {
138215
+ symbolGraph;
138118
138216
  constructor(symbolGraph) {
138119
- this.compressor = new CodeCompressor;
138120
138217
  this.symbolGraph = symbolGraph;
138121
- eventBus.subscribe("indexing:started", ({ projectId, projectPath: projectPath2 }) => {
138122
- this.projectRootCache.delete(projectId);
138123
- this.evictOldest(this.projectRootCache);
138124
- this.projectRootCache.set(projectId, projectPath2);
138125
- });
138126
138218
  }
138127
- async handle(params) {
138128
- const p = params;
138129
- const shouldCompress = p.compress !== false;
138130
- const targetRatio = p.targetRatio || 0.3;
138131
- const format = p.format || "json";
138132
- const { fields } = p;
138133
- const includeSymbols = p.includeSymbols !== false;
138134
- const includeImports = p.includeImports !== false;
138135
- try {
138136
- const resolved = await this.resolveFilePath(p.filePath, p.projectId);
138137
- if (resolved === null) {
138138
- return {
138139
- success: false,
138140
- error: "Relative filePath requires a projectId (to resolve against the workspace) or an absolute path."
138219
+ async extractMetadata(content, filePath, options) {
138220
+ const lines = content.split(`
138221
+ `);
138222
+ const language = this.detectLanguage(filePath);
138223
+ const metadata = {
138224
+ totalLines: lines.length,
138225
+ language
138226
+ };
138227
+ if (options.includeImports && language) {
138228
+ metadata.imports = this.extractImports(lines, language);
138229
+ }
138230
+ if (options.includeSymbols && this.symbolGraph && options.projectId) {
138231
+ try {
138232
+ const queryPath = options.relativePath || filePath;
138233
+ const { definitions } = await this.symbolGraph.listDefinitions(options.projectId, {
138234
+ file: queryPath,
138235
+ limit: 100
138236
+ });
138237
+ metadata.symbols = {
138238
+ definitions: definitions.length,
138239
+ references: 0
138141
138240
  };
138241
+ } catch (error51) {
138242
+ logger.debug("Failed to get symbol metadata", { filePath, error: error51 });
138142
138243
  }
138143
- const filePath = resolved;
138144
- const containment = await this.checkPathContainment(filePath, p.projectId);
138145
- if (!containment.allowed) {
138146
- return {
138147
- success: false,
138148
- error: containment.error
138149
- };
138244
+ }
138245
+ return metadata;
138246
+ }
138247
+ detectLanguage(filePath) {
138248
+ const ext2 = path21.extname(filePath).toLowerCase();
138249
+ const languageMap2 = {
138250
+ ".ts": "TypeScript",
138251
+ ".tsx": "TypeScript",
138252
+ ".js": "JavaScript",
138253
+ ".jsx": "JavaScript",
138254
+ ".vue": "Vue",
138255
+ ".py": "Python",
138256
+ ".go": "Go",
138257
+ ".rs": "Rust",
138258
+ ".java": "Java",
138259
+ ".cpp": "C++",
138260
+ ".c": "C",
138261
+ ".h": "C",
138262
+ ".hpp": "C++",
138263
+ ".cs": "C#",
138264
+ ".rb": "Ruby",
138265
+ ".php": "PHP",
138266
+ ".swift": "Swift",
138267
+ ".kt": "Kotlin",
138268
+ ".kts": "Kotlin",
138269
+ ".scala": "Scala",
138270
+ ".md": "Markdown",
138271
+ ".json": "JSON",
138272
+ ".yaml": "YAML",
138273
+ ".yml": "YAML",
138274
+ ".xml": "XML",
138275
+ ".html": "HTML",
138276
+ ".css": "CSS",
138277
+ ".scss": "SCSS",
138278
+ ".sql": "SQL",
138279
+ ".sh": "Shell",
138280
+ ".bash": "Shell"
138281
+ };
138282
+ return languageMap2[ext2];
138283
+ }
138284
+ extractImports(lines, language) {
138285
+ const imports = [];
138286
+ const importPatterns = {
138287
+ TypeScript: /^(import\s+.*?from\s+['"]|import\s+['"])/,
138288
+ JavaScript: /^(import\s+.*?from\s+['"]|import\s+['"]|require\s*\(\s*['"])/,
138289
+ Python: /^(import\s+|from\s+\S+\s+import)/,
138290
+ Go: /^import\s+/,
138291
+ Java: /^import\s+/,
138292
+ Rust: /^use\s+/
138293
+ };
138294
+ const pattern = importPatterns[language];
138295
+ if (!pattern)
138296
+ return imports;
138297
+ for (const line of lines) {
138298
+ const trimmed = line.trim();
138299
+ if (pattern.test(trimmed)) {
138300
+ imports.push(trimmed);
138150
138301
  }
138151
- const relativePath = p.filePath;
138152
- const range = this.calculateRange(p);
138153
- const { content, metadata } = await this.readFileWithCache(filePath, {
138154
- includeSymbols,
138155
- includeImports,
138156
- projectId: p.projectId,
138157
- relativePath
138158
- });
138159
- const lines = content.split(`
138302
+ }
138303
+ return imports;
138304
+ }
138305
+ }
138306
+ var init_file_metadata = __esm(() => {
138307
+ init_dist();
138308
+ });
138309
+
138310
+ // ../../packages/core/dist/services/file-read/line-range.js
138311
+ function calculateRange(params) {
138312
+ if (params.lineStart !== undefined && params.lineEnd !== undefined) {
138313
+ return {
138314
+ start: Math.max(1, params.lineStart),
138315
+ end: params.lineEnd
138316
+ };
138317
+ }
138318
+ if (params.offset !== undefined) {
138319
+ const offset = Math.max(1, params.offset);
138320
+ const limit = params.limit || 1000;
138321
+ return {
138322
+ start: offset,
138323
+ end: offset + limit - 1
138324
+ };
138325
+ }
138326
+ return {
138327
+ start: 1,
138328
+ end: Infinity
138329
+ };
138330
+ }
138331
+ function adjustRange(range, totalLines) {
138332
+ const start = Math.max(1, Math.min(range.start, totalLines));
138333
+ const end = range.end === Infinity ? totalLines : Math.min(range.end, totalLines);
138334
+ return { start, end };
138335
+ }
138336
+ function extractLines(lines, range) {
138337
+ const start = range.start - 1;
138338
+ const end = range.end;
138339
+ const selectedLines = lines.slice(start, end);
138340
+ return selectedLines.map((line, index) => {
138341
+ const lineNum = start + index + 1;
138342
+ return `${lineNum.toString().padStart(6, " ")}: ${line}`;
138343
+ }).join(`
138160
138344
  `);
138161
- const totalLines = lines.length;
138162
- const adjustedRange = this.adjustRange(range, totalLines);
138163
- let selectedContent = this.extractLines(lines, adjustedRange);
138164
- let selectedLineCount = selectedContent.split(`
138345
+ }
138346
+ function selectLines(lines, range) {
138347
+ let content = extractLines(lines, range);
138348
+ let lineCount = content.split(`
138165
138349
  `).length;
138166
- let source_clipped = false;
138167
- if (selectedLineCount > MASSA_AI_READ_FILE_MAX_LINES) {
138168
- const cappedLines = lines.slice(adjustedRange.start - 1, adjustedRange.start - 1 + MASSA_AI_READ_FILE_MAX_LINES);
138169
- selectedContent = cappedLines.join(`
138350
+ let clipped = false;
138351
+ if (lineCount > MASSA_AI_READ_FILE_MAX_LINES) {
138352
+ const cappedLines = lines.slice(range.start - 1, range.start - 1 + MASSA_AI_READ_FILE_MAX_LINES);
138353
+ content = cappedLines.join(`
138170
138354
  `);
138171
- selectedLineCount = selectedContent.split(`
138355
+ lineCount = content.split(`
138172
138356
  `).length;
138173
- source_clipped = true;
138174
- }
138175
- const shouldAutoCompress = shouldCompress && selectedLineCount > 100 && targetRatio < 1;
138176
- const result = {
138177
- filePath: p.filePath,
138178
- absolutePath: filePath,
138179
- lineRange: {
138180
- requested: {
138181
- start: range.start,
138182
- end: range.end === Infinity ? null : range.end
138183
- },
138184
- actual: {
138185
- start: adjustedRange.start,
138186
- end: source_clipped ? adjustedRange.start + selectedLineCount - 1 : adjustedRange.end,
138187
- total: totalLines
138188
- },
138189
- selected: selectedLineCount
138190
- },
138191
- source_clipped,
138192
- metadata: {
138193
- totalLines,
138194
- language: metadata.language,
138195
- ...metadata.symbols && { symbols: metadata.symbols },
138196
- ...metadata.imports && { imports: metadata.imports }
138197
- },
138198
- compressed: shouldAutoCompress,
138199
- recommendations: []
138200
- };
138201
- if (shouldAutoCompress) {
138202
- const compressed = await this.compressor.compress(selectedContent, "code_structure");
138203
- const originalTokens = estimateTokens(selectedContent, "code");
138204
- const compressedTokens = estimateTokens(compressed.compressed, "code");
138205
- const actualRatio = compressedTokens / originalTokens;
138206
- result.content = compressed.compressed;
138207
- result.tokens = {
138208
- original: originalTokens,
138209
- compressed: compressedTokens,
138210
- saved: originalTokens - compressedTokens,
138211
- savingsPercent: Math.round((1 - actualRatio) * 100)
138212
- };
138213
- result.compressionRatio = actualRatio;
138214
- result.recommendations.push(`\u2713 Auto-compressed ${selectedLineCount} lines (${result.tokens.savingsPercent}% reduction)`);
138215
- } else {
138216
- result.content = selectedContent;
138217
- result.tokens = {
138218
- original: estimateTokens(selectedContent, "code"),
138219
- compressed: estimateTokens(selectedContent, "code"),
138220
- saved: 0,
138221
- savingsPercent: 0
138222
- };
138223
- if (selectedLineCount > 100) {
138224
- result.recommendations.push("\uD83D\uDCA1 Content > 100 lines. Consider compress=true for token savings");
138225
- }
138226
- }
138227
- if (range.start === 1 && range.end === Infinity) {
138228
- result.recommendations.push("\uD83D\uDCA1 Use lineStart/lineEnd or offset/limit to read specific sections (60% token savings)");
138229
- }
138230
- if (metadata.symbols && metadata.symbols.definitions > 0) {
138231
- result.recommendations.push(`\uD83D\uDCA1 Use get_references() to find usages of ${metadata.symbols.definitions} symbols in this file`);
138232
- }
138233
- return serializeToolResponse(result, { format, fields });
138234
- } catch (error51) {
138235
- logger.error("Failed to read file", error51, {
138236
- filePath: p.filePath
138237
- });
138238
- return {
138239
- success: false,
138240
- error: `Failed to read file: ${error51.message}`
138241
- };
138242
- }
138357
+ clipped = true;
138358
+ }
138359
+ return { content, lineCount, clipped };
138360
+ }
138361
+ var MASSA_AI_READ_FILE_MAX_LINES;
138362
+ var init_line_range = __esm(() => {
138363
+ MASSA_AI_READ_FILE_MAX_LINES = (() => {
138364
+ const v = Number(process.env.MASSA_AI_READ_FILE_MAX_LINES);
138365
+ return Number.isFinite(v) && v > 0 ? Math.floor(v) : 500;
138366
+ })();
138367
+ });
138368
+
138369
+ // ../../packages/core/dist/services/file-read/path-containment.js
138370
+ import path22 from "path";
138371
+
138372
+ class PathContainment {
138373
+ projectRoots;
138374
+ constructor(projectRoots) {
138375
+ this.projectRoots = projectRoots;
138243
138376
  }
138244
138377
  async resolveFilePath(filePath, projectId) {
138245
- if (path20.isAbsolute(filePath)) {
138246
- return path20.resolve(filePath);
138378
+ if (path22.isAbsolute(filePath)) {
138379
+ return path22.resolve(filePath);
138247
138380
  }
138248
138381
  if (projectId) {
138249
- const root = await this.getProjectRoot(projectId);
138382
+ const root = await this.projectRoots.getProjectRoot(projectId);
138250
138383
  if (root) {
138251
138384
  const cleaned = sanitizeFilePath(filePath);
138252
- return path20.resolve(root, cleaned);
138385
+ return path22.resolve(root, cleaned);
138253
138386
  }
138254
138387
  return null;
138255
138388
  }
@@ -138258,19 +138391,19 @@ class ReadFileTool {
138258
138391
  async checkPathContainment(absoluteFilePath, projectId) {
138259
138392
  const roots = [];
138260
138393
  if (projectId) {
138261
- const root = await this.getProjectRoot(projectId);
138394
+ const root = await this.projectRoots.getProjectRoot(projectId);
138262
138395
  if (root)
138263
- roots.push(path20.resolve(root));
138396
+ roots.push(path22.resolve(root));
138264
138397
  }
138265
- roots.push(path20.resolve(process.cwd()));
138398
+ roots.push(path22.resolve(process.cwd()));
138266
138399
  const envRoots = (process.env.MASSA_AI_READ_FILE_ROOTS ?? "").split(":").map((s) => s.trim()).filter((s) => s.length > 0);
138267
138400
  for (const extra of envRoots) {
138268
- roots.push(path20.resolve(extra));
138401
+ roots.push(path22.resolve(extra));
138269
138402
  }
138270
- const target = path20.resolve(absoluteFilePath);
138403
+ const target = path22.resolve(absoluteFilePath);
138271
138404
  for (const root of roots) {
138272
- const rel = path20.relative(root, target);
138273
- if (rel !== "" && !rel.startsWith("..") && !path20.isAbsolute(rel)) {
138405
+ const rel = path22.relative(root, target);
138406
+ if (rel !== "" && !rel.startsWith("..") && !path22.isAbsolute(rel)) {
138274
138407
  return { allowed: true };
138275
138408
  }
138276
138409
  if (rel === "")
@@ -138286,6 +138419,23 @@ ${validRootsList}
138286
138419
  ` + `Provide a filePath that resolves under one of these roots.`
138287
138420
  };
138288
138421
  }
138422
+ }
138423
+ var init_path_containment = __esm(() => {
138424
+ init_dist();
138425
+ });
138426
+
138427
+ // ../../packages/core/dist/services/file-read/project-root-cache.js
138428
+ class ProjectRootCache {
138429
+ projectRootCache = new Map;
138430
+ ROOT_CACHE_TTL = 300000;
138431
+ PROJECT_ROOT_CACHE_MAX_ENTRIES = 512;
138432
+ constructor() {
138433
+ eventBus.subscribe("indexing:started", ({ projectId, projectPath: projectPath2 }) => {
138434
+ this.projectRootCache.delete(projectId);
138435
+ evictOldest(this.projectRootCache, this.PROJECT_ROOT_CACHE_MAX_ENTRIES - 1);
138436
+ this.projectRootCache.set(projectId, projectPath2);
138437
+ });
138438
+ }
138289
138439
  async getProjectRoot(projectId) {
138290
138440
  const cached2 = this.projectRootCache.get(projectId);
138291
138441
  if (cached2 !== undefined) {
@@ -138296,7 +138446,7 @@ ${validRootsList}
138296
138446
  try {
138297
138447
  const workspace = await workspaceManager.getWorkspace(projectId);
138298
138448
  if (workspace?.project_path) {
138299
- this.evictOldest(this.projectRootCache);
138449
+ evictOldest(this.projectRootCache, this.PROJECT_ROOT_CACHE_MAX_ENTRIES - 1);
138300
138450
  this.projectRootCache.set(projectId, workspace.project_path);
138301
138451
  return workspace.project_path;
138302
138452
  }
@@ -138305,182 +138455,236 @@ ${validRootsList}
138305
138455
  }
138306
138456
  return null;
138307
138457
  }
138308
- evictOldest(cache) {
138309
- while (cache.size >= this.FILE_CACHE_MAX_ENTRIES) {
138310
- const oldest = cache.keys().next().value;
138311
- if (oldest === undefined)
138312
- break;
138313
- cache.delete(oldest);
138314
- }
138315
- }
138316
- calculateRange(params) {
138317
- if (params.lineStart !== undefined && params.lineEnd !== undefined) {
138458
+ }
138459
+ var init_project_root_cache = __esm(() => {
138460
+ init_dist();
138461
+ init_event_bus();
138462
+ init_workspace_manager();
138463
+ });
138464
+
138465
+ // ../../packages/core/dist/services/file-read/read-file.service.js
138466
+ function readFileOptions(p) {
138467
+ const shouldCompress = p.compress !== false;
138468
+ const targetRatio = p.targetRatio || 0.3;
138469
+ const format = p.format || "json";
138470
+ const { fields } = p;
138471
+ const includeSymbols = p.includeSymbols !== false;
138472
+ const includeImports = p.includeImports !== false;
138473
+ return { shouldCompress, targetRatio, format, fields, includeSymbols, includeImports };
138474
+ }
138475
+
138476
+ class ReadFileService {
138477
+ compressor;
138478
+ symbolGraph;
138479
+ projectRoots;
138480
+ pathContainment;
138481
+ fileMetadata;
138482
+ fileContent;
138483
+ constructor(symbolGraph) {
138484
+ this.compressor = new CodeCompressor;
138485
+ this.symbolGraph = symbolGraph;
138486
+ this.projectRoots = new ProjectRootCache;
138487
+ this.pathContainment = new PathContainment(this.projectRoots);
138488
+ this.fileMetadata = new FileMetadataExtractor(symbolGraph);
138489
+ this.fileContent = new FileContentCache((content, filePath, options) => this.fileMetadata.extractMetadata(content, filePath, options));
138490
+ }
138491
+ async read(p, options) {
138492
+ const { shouldCompress, targetRatio, includeSymbols, includeImports } = options;
138493
+ const resolved = await this.pathContainment.resolveFilePath(p.filePath, p.projectId);
138494
+ if (resolved === null) {
138318
138495
  return {
138319
- start: Math.max(1, params.lineStart),
138320
- end: params.lineEnd
138496
+ ok: false,
138497
+ error: "Relative filePath requires a projectId (to resolve against the workspace) or an absolute path."
138321
138498
  };
138322
138499
  }
138323
- if (params.offset !== undefined) {
138324
- const offset = Math.max(1, params.offset);
138325
- const limit = params.limit || 1000;
138500
+ const filePath = resolved;
138501
+ const containment = await this.pathContainment.checkPathContainment(filePath, p.projectId);
138502
+ if (!containment.allowed) {
138326
138503
  return {
138327
- start: offset,
138328
- end: offset + limit - 1
138504
+ ok: false,
138505
+ error: containment.error
138329
138506
  };
138330
138507
  }
138331
- return {
138332
- start: 1,
138333
- end: Infinity
138334
- };
138335
- }
138336
- adjustRange(range, totalLines) {
138337
- const start = Math.max(1, Math.min(range.start, totalLines));
138338
- const end = range.end === Infinity ? totalLines : Math.min(range.end, totalLines);
138339
- return { start, end };
138340
- }
138341
- async readFileWithCache(filePath, options) {
138342
- const cacheKey = JSON.stringify({
138343
- filePath,
138344
- includeSymbols: options.includeSymbols ?? false,
138345
- includeImports: options.includeImports ?? false,
138346
- projectId: options.projectId ?? null,
138347
- relativePath: options.relativePath ?? null
138348
- });
138349
- const cached2 = this.fileCache.get(cacheKey);
138350
- if (cached2 && Date.now() - cached2.timestamp < this.CACHE_TTL) {
138351
- logger.debug("File cache hit", { filePath });
138352
- this.fileCache.delete(cacheKey);
138353
- this.fileCache.set(cacheKey, cached2);
138354
- if (!cached2.metadata) {
138355
- const metadata2 = await this.extractMetadata(cached2.content, filePath, options);
138356
- this.fileCache.set(cacheKey, { ...cached2, metadata: metadata2 });
138357
- return { content: cached2.content, metadata: metadata2 };
138358
- }
138359
- return {
138360
- content: cached2.content,
138361
- metadata: cached2.metadata
138362
- };
138363
- }
138364
- const content = await fs10.readFile(filePath, "utf-8");
138365
- const metadata = await this.extractMetadata(content, filePath, options);
138366
- this.evictOldest(this.fileCache);
138367
- this.fileCache.set(cacheKey, {
138368
- content,
138369
- timestamp: Date.now(),
138370
- metadata
138508
+ const relativePath = p.filePath;
138509
+ const range = calculateRange(p);
138510
+ const { content, metadata } = await this.fileContent.readFileWithCache(filePath, {
138511
+ includeSymbols,
138512
+ includeImports,
138513
+ projectId: p.projectId,
138514
+ relativePath
138371
138515
  });
138372
- logger.debug("File read and cached", { filePath });
138373
- return { content, metadata };
138374
- }
138375
- async extractMetadata(content, filePath, options) {
138376
138516
  const lines = content.split(`
138377
138517
  `);
138378
- const language = this.detectLanguage(filePath);
138379
- const metadata = {
138380
- totalLines: lines.length,
138381
- language
138518
+ const totalLines = lines.length;
138519
+ const adjustedRange = adjustRange(range, totalLines);
138520
+ const { content: selectedContent, lineCount: selectedLineCount, clipped: source_clipped } = selectLines(lines, adjustedRange);
138521
+ const shouldAutoCompress = shouldCompress && selectedLineCount > 100 && targetRatio < 1;
138522
+ const result = {
138523
+ filePath: p.filePath,
138524
+ absolutePath: filePath,
138525
+ lineRange: {
138526
+ requested: {
138527
+ start: range.start,
138528
+ end: range.end === Infinity ? null : range.end
138529
+ },
138530
+ actual: {
138531
+ start: adjustedRange.start,
138532
+ end: source_clipped ? adjustedRange.start + selectedLineCount - 1 : adjustedRange.end,
138533
+ total: totalLines
138534
+ },
138535
+ selected: selectedLineCount
138536
+ },
138537
+ source_clipped,
138538
+ metadata: {
138539
+ totalLines,
138540
+ language: metadata.language,
138541
+ ...metadata.symbols && { symbols: metadata.symbols },
138542
+ ...metadata.imports && { imports: metadata.imports }
138543
+ },
138544
+ compressed: shouldAutoCompress,
138545
+ recommendations: []
138382
138546
  };
138383
- if (options.includeImports && language) {
138384
- metadata.imports = this.extractImports(lines, language);
138385
- }
138386
- if (options.includeSymbols && this.symbolGraph && options.projectId) {
138387
- try {
138388
- const queryPath = options.relativePath || filePath;
138389
- const { definitions } = await this.symbolGraph.listDefinitions(options.projectId, {
138390
- file: queryPath,
138391
- limit: 100
138392
- });
138393
- metadata.symbols = {
138394
- definitions: definitions.length,
138395
- references: 0
138396
- };
138397
- } catch (error51) {
138398
- logger.debug("Failed to get symbol metadata", { filePath, error: error51 });
138547
+ if (shouldAutoCompress) {
138548
+ const compressed = await this.compressor.compress(selectedContent, "code_structure");
138549
+ const originalTokens = estimateTokens(selectedContent, "code");
138550
+ const compressedTokens = estimateTokens(compressed.compressed, "code");
138551
+ const actualRatio = compressedTokens / originalTokens;
138552
+ result.content = compressed.compressed;
138553
+ result.tokens = {
138554
+ original: originalTokens,
138555
+ compressed: compressedTokens,
138556
+ saved: originalTokens - compressedTokens,
138557
+ savingsPercent: Math.round((1 - actualRatio) * 100)
138558
+ };
138559
+ result.compressionRatio = actualRatio;
138560
+ result.recommendations.push(`\u2713 Auto-compressed ${selectedLineCount} lines (${result.tokens.savingsPercent}% reduction)`);
138561
+ } else {
138562
+ result.content = selectedContent;
138563
+ result.tokens = {
138564
+ original: estimateTokens(selectedContent, "code"),
138565
+ compressed: estimateTokens(selectedContent, "code"),
138566
+ saved: 0,
138567
+ savingsPercent: 0
138568
+ };
138569
+ if (selectedLineCount > 100) {
138570
+ result.recommendations.push("\uD83D\uDCA1 Content > 100 lines. Consider compress=true for token savings");
138399
138571
  }
138400
138572
  }
138401
- return metadata;
138402
- }
138403
- extractLines(lines, range) {
138404
- const start = range.start - 1;
138405
- const end = range.end;
138406
- const selectedLines = lines.slice(start, end);
138407
- return selectedLines.map((line, index) => {
138408
- const lineNum = start + index + 1;
138409
- return `${lineNum.toString().padStart(6, " ")}: ${line}`;
138410
- }).join(`
138411
- `);
138573
+ if (range.start === 1 && range.end === Infinity) {
138574
+ result.recommendations.push("\uD83D\uDCA1 Use lineStart/lineEnd or offset/limit to read specific sections (60% token savings)");
138575
+ }
138576
+ if (metadata.symbols && metadata.symbols.definitions > 0) {
138577
+ result.recommendations.push(`\uD83D\uDCA1 Use get_references() to find usages of ${metadata.symbols.definitions} symbols in this file`);
138578
+ }
138579
+ return { ok: true, data: result };
138412
138580
  }
138413
- detectLanguage(filePath) {
138414
- const ext2 = path20.extname(filePath).toLowerCase();
138415
- const languageMap2 = {
138416
- ".ts": "TypeScript",
138417
- ".tsx": "TypeScript",
138418
- ".js": "JavaScript",
138419
- ".jsx": "JavaScript",
138420
- ".vue": "Vue",
138421
- ".py": "Python",
138422
- ".go": "Go",
138423
- ".rs": "Rust",
138424
- ".java": "Java",
138425
- ".cpp": "C++",
138426
- ".c": "C",
138427
- ".h": "C",
138428
- ".hpp": "C++",
138429
- ".cs": "C#",
138430
- ".rb": "Ruby",
138431
- ".php": "PHP",
138432
- ".swift": "Swift",
138433
- ".kt": "Kotlin",
138434
- ".kts": "Kotlin",
138435
- ".scala": "Scala",
138436
- ".md": "Markdown",
138437
- ".json": "JSON",
138438
- ".yaml": "YAML",
138439
- ".yml": "YAML",
138440
- ".xml": "XML",
138441
- ".html": "HTML",
138442
- ".css": "CSS",
138443
- ".scss": "SCSS",
138444
- ".sql": "SQL",
138445
- ".sh": "Shell",
138446
- ".bash": "Shell"
138447
- };
138448
- return languageMap2[ext2];
138581
+ }
138582
+ var init_read_file_service = __esm(() => {
138583
+ init_dist();
138584
+ init_code_compressor();
138585
+ init_file_content_cache();
138586
+ init_file_metadata();
138587
+ init_line_range();
138588
+ init_path_containment();
138589
+ init_project_root_cache();
138590
+ });
138591
+
138592
+ // ../../packages/core/dist/tools/read_file.js
138593
+ class ReadFileTool {
138594
+ name = "read_file";
138595
+ description = "Read file with automatic compression, caching, and symbol metadata. " + "Use with search results for 60% token savings.";
138596
+ inputSchema = {
138597
+ type: "object",
138598
+ properties: {
138599
+ filePath: {
138600
+ type: "string",
138601
+ description: "File path (absolute or relative to project root)"
138602
+ },
138603
+ projectId: {
138604
+ type: "string",
138605
+ description: "Project ID for symbol metadata (optional)"
138606
+ },
138607
+ offset: {
138608
+ type: "number",
138609
+ description: "Start line number (1-indexed)"
138610
+ },
138611
+ limit: {
138612
+ type: "number",
138613
+ description: "Number of lines to read"
138614
+ },
138615
+ lineStart: {
138616
+ type: "number",
138617
+ description: "Start line (alternative to offset)"
138618
+ },
138619
+ lineEnd: {
138620
+ type: "number",
138621
+ description: "End line (alternative to limit)"
138622
+ },
138623
+ compress: {
138624
+ type: "boolean",
138625
+ description: "Auto-compress content > 100 lines (default: true)",
138626
+ default: true
138627
+ },
138628
+ targetRatio: {
138629
+ type: "number",
138630
+ description: "Compression target ratio (0.3 = 70% reduction)",
138631
+ default: 0.3
138632
+ },
138633
+ format: {
138634
+ type: "string",
138635
+ enum: ["json", "toon"],
138636
+ description: "Output format",
138637
+ default: "json"
138638
+ },
138639
+ fields: {
138640
+ type: "array",
138641
+ items: { type: "string" },
138642
+ description: "Projection \u2014 keep only these keys (dotted paths supported, e.g. ['nodes.symbol']). Absent/empty \u2192 full data."
138643
+ },
138644
+ includeSymbols: {
138645
+ type: "boolean",
138646
+ description: "Include symbol metadata from graph (default: true)",
138647
+ default: true
138648
+ },
138649
+ includeImports: {
138650
+ type: "boolean",
138651
+ description: "Extract and show import statements (default: true)",
138652
+ default: true
138653
+ }
138654
+ },
138655
+ required: ["filePath"]
138656
+ };
138657
+ service;
138658
+ constructor(symbolGraph) {
138659
+ this.service = new ReadFileService(symbolGraph);
138449
138660
  }
138450
- extractImports(lines, language) {
138451
- const imports = [];
138452
- const importPatterns = {
138453
- TypeScript: /^(import\s+.*?from\s+['"]|import\s+['"])/,
138454
- JavaScript: /^(import\s+.*?from\s+['"]|import\s+['"]|require\s*\(\s*['"])/,
138455
- Python: /^(import\s+|from\s+\S+\s+import)/,
138456
- Go: /^import\s+/,
138457
- Java: /^import\s+/,
138458
- Rust: /^use\s+/
138459
- };
138460
- const pattern = importPatterns[language];
138461
- if (!pattern)
138462
- return imports;
138463
- for (const line of lines) {
138464
- const trimmed = line.trim();
138465
- if (pattern.test(trimmed)) {
138466
- imports.push(trimmed);
138661
+ async handle(params) {
138662
+ const p = params;
138663
+ const options = readFileOptions(p);
138664
+ try {
138665
+ const outcome2 = await this.service.read(p, options);
138666
+ if (!outcome2.ok) {
138667
+ return { success: false, error: outcome2.error };
138467
138668
  }
138669
+ return serializeToolResponse(outcome2.data, {
138670
+ format: options.format,
138671
+ fields: options.fields
138672
+ });
138673
+ } catch (error51) {
138674
+ logger.error("Failed to read file", error51, {
138675
+ filePath: p.filePath
138676
+ });
138677
+ return {
138678
+ success: false,
138679
+ error: `Failed to read file: ${error51.message}`
138680
+ };
138468
138681
  }
138469
- return imports;
138470
138682
  }
138471
138683
  }
138472
- var MASSA_AI_READ_FILE_MAX_LINES;
138473
138684
  var init_read_file = __esm(() => {
138474
138685
  init_dist();
138475
- init_dist();
138476
- init_code_compressor();
138477
138686
  init_serialize();
138478
- init_event_bus();
138479
- init_workspace_manager();
138480
- MASSA_AI_READ_FILE_MAX_LINES = (() => {
138481
- const v = Number(process.env.MASSA_AI_READ_FILE_MAX_LINES);
138482
- return Number.isFinite(v) && v > 0 ? Math.floor(v) : 500;
138483
- })();
138687
+ init_read_file_service();
138484
138688
  });
138485
138689
 
138486
138690
  // ../../packages/core/dist/tools/execute.js
@@ -141415,7 +141619,7 @@ var init_l1_memory_cache = __esm(() => {
141415
141619
  // ../../packages/core/dist/services/health/local-health-checker.js
141416
141620
  import fs11 from "fs/promises";
141417
141621
  import { existsSync as existsSync3 } from "fs";
141418
- import path21 from "path";
141622
+ import path23 from "path";
141419
141623
 
141420
141624
  class LocalHealthChecker {
141421
141625
  ollamaBaseUrl = process.env.OLLAMA_BASE_URL || "http://localhost:11434";
@@ -141450,7 +141654,7 @@ class LocalHealthChecker {
141450
141654
  try {
141451
141655
  if (!existsSync3(this.dataDir))
141452
141656
  await fs11.mkdir(this.dataDir, { recursive: true });
141453
- const probe2 = path21.join(this.dataDir, ".health-check-test");
141657
+ const probe2 = path23.join(this.dataDir, ".health-check-test");
141454
141658
  await fs11.writeFile(probe2, "ok");
141455
141659
  await fs11.unlink(probe2);
141456
141660
  return { available: true, latency: Date.now() - start, details: { path: this.dataDir, writable: true } };
@@ -143393,7 +143597,7 @@ var init_scheduler2 = __esm(() => {
143393
143597
  // ../../packages/core/dist/services/pricing/models-dev-client.js
143394
143598
  import fs12 from "fs/promises";
143395
143599
  import { existsSync as existsSync4 } from "fs";
143396
- import path22 from "path";
143600
+ import path24 from "path";
143397
143601
  function getModelsDevClient() {
143398
143602
  if (!clientInstance) {
143399
143603
  clientInstance = new ModelsDevClient;
@@ -143413,7 +143617,7 @@ var init_models_dev_client = __esm(() => {
143413
143617
  memoryCacheTimestamp = 0;
143414
143618
  getLocalCachePath() {
143415
143619
  const dataDir = config2.get("dataDir");
143416
- return path22.join(dataDir, ModelsDevClient.LOCAL_CACHE_FILE);
143620
+ return path24.join(dataDir, ModelsDevClient.LOCAL_CACHE_FILE);
143417
143621
  }
143418
143622
  async loadLocalCache() {
143419
143623
  const cachePath = this.getLocalCachePath();
@@ -143448,7 +143652,7 @@ var init_models_dev_client = __esm(() => {
143448
143652
  async saveLocalCache(models) {
143449
143653
  const cachePath = this.getLocalCachePath();
143450
143654
  try {
143451
- const dir = path22.dirname(cachePath);
143655
+ const dir = path24.dirname(cachePath);
143452
143656
  await fs12.mkdir(dir, { recursive: true });
143453
143657
  const data = {
143454
143658
  timestamp: Date.now(),
@@ -149339,33 +149543,33 @@ var require_URL = __commonJS((exports, module) => {
149339
149543
  else
149340
149544
  return basepath.substring(0, lastslash + 1) + refpath;
149341
149545
  }
149342
- function remove_dot_segments(path23) {
149343
- if (!path23)
149344
- return path23;
149546
+ function remove_dot_segments(path25) {
149547
+ if (!path25)
149548
+ return path25;
149345
149549
  var output = "";
149346
- while (path23.length > 0) {
149347
- if (path23 === "." || path23 === "..") {
149348
- path23 = "";
149550
+ while (path25.length > 0) {
149551
+ if (path25 === "." || path25 === "..") {
149552
+ path25 = "";
149349
149553
  break;
149350
149554
  }
149351
- var twochars = path23.substring(0, 2);
149352
- var threechars = path23.substring(0, 3);
149353
- var fourchars = path23.substring(0, 4);
149555
+ var twochars = path25.substring(0, 2);
149556
+ var threechars = path25.substring(0, 3);
149557
+ var fourchars = path25.substring(0, 4);
149354
149558
  if (threechars === "../") {
149355
- path23 = path23.substring(3);
149559
+ path25 = path25.substring(3);
149356
149560
  } else if (twochars === "./") {
149357
- path23 = path23.substring(2);
149561
+ path25 = path25.substring(2);
149358
149562
  } else if (threechars === "/./") {
149359
- path23 = "/" + path23.substring(3);
149360
- } else if (twochars === "/." && path23.length === 2) {
149361
- path23 = "/";
149362
- } else if (fourchars === "/../" || threechars === "/.." && path23.length === 3) {
149363
- path23 = "/" + path23.substring(4);
149563
+ path25 = "/" + path25.substring(3);
149564
+ } else if (twochars === "/." && path25.length === 2) {
149565
+ path25 = "/";
149566
+ } else if (fourchars === "/../" || threechars === "/.." && path25.length === 3) {
149567
+ path25 = "/" + path25.substring(4);
149364
149568
  output = output.replace(/\/?[^\/]*$/, "");
149365
149569
  } else {
149366
- var segment = path23.match(/(\/?([^\/]*))/)[0];
149570
+ var segment = path25.match(/(\/?([^\/]*))/)[0];
149367
149571
  output += segment;
149368
- path23 = path23.substring(segment.length);
149572
+ path25 = path25.substring(segment.length);
149369
149573
  }
149370
149574
  }
149371
149575
  return output;
@@ -161435,21 +161639,21 @@ function jsonToKeyPathChunks(value, label = "$") {
161435
161639
  walk(value, label, out);
161436
161640
  return out;
161437
161641
  }
161438
- function walk(val, path23, out) {
161642
+ function walk(val, path25, out) {
161439
161643
  if (val === null || val === undefined)
161440
161644
  return;
161441
161645
  if (Array.isArray(val)) {
161442
161646
  if (val.length === 0) {
161443
- out.push({ path: path23, content: `**${path23}** = _[]_` });
161647
+ out.push({ path: path25, content: `**${path25}** = _[]_` });
161444
161648
  return;
161445
161649
  }
161446
161650
  if (val.every((v) => v !== null && typeof v === "object")) {
161447
- val.forEach((v, i) => walk(v, `${path23}[${i}]`, out));
161651
+ val.forEach((v, i) => walk(v, `${path25}[${i}]`, out));
161448
161652
  return;
161449
161653
  }
161450
161654
  const items = val.map((v) => `- \`${String(v)}\``).join(`
161451
161655
  `);
161452
- out.push({ path: path23, content: `**${path23}**
161656
+ out.push({ path: path25, content: `**${path25}**
161453
161657
 
161454
161658
  ${items}` });
161455
161659
  return;
@@ -161457,16 +161661,16 @@ ${items}` });
161457
161661
  if (typeof val === "object") {
161458
161662
  const entries = Object.entries(val);
161459
161663
  if (entries.length === 0) {
161460
- out.push({ path: path23, content: `**${path23}** = _{}_` });
161664
+ out.push({ path: path25, content: `**${path25}** = _{}_` });
161461
161665
  return;
161462
161666
  }
161463
161667
  for (const [k, v] of entries) {
161464
161668
  const safeKey = /^[A-Za-z_$][\w$]*$/.test(k) ? k : JSON.stringify(k);
161465
- walk(v, `${path23}.${safeKey}`, out);
161669
+ walk(v, `${path25}.${safeKey}`, out);
161466
161670
  }
161467
161671
  return;
161468
161672
  }
161469
- out.push({ path: path23, content: `**${path23}** = \`${String(val)}\`` });
161673
+ out.push({ path: path25, content: `**${path25}** = \`${String(val)}\`` });
161470
161674
  }
161471
161675
  var gfm, STRIP_SELECTORS, tdCache = null;
161472
161676
  var init_html_to_md = __esm(() => {
@@ -161732,12 +161936,7 @@ class WebController {
161732
161936
  markIndexed: (key, ts) => {
161733
161937
  this.cache.delete(key);
161734
161938
  this.cache.set(key, ts);
161735
- while (this.cache.size > WEB_CACHE_MAX_ENTRIES) {
161736
- const oldest = this.cache.keys().next().value;
161737
- if (oldest === undefined)
161738
- break;
161739
- this.cache.delete(oldest);
161740
- }
161939
+ evictOldest(this.cache, WEB_CACHE_MAX_ENTRIES);
161741
161940
  }
161742
161941
  };
161743
161942
  }
@@ -162134,13 +162333,20 @@ class HookService {
162134
162333
  });
162135
162334
  const id = this.idFactory();
162136
162335
  this.queue.enqueue(async () => {
162336
+ const scrub = scrubCredentials(JSON.stringify(ev.payload));
162337
+ if (scrub.total > 0) {
162338
+ logger.debug("hook payload redacted before persist", {
162339
+ redactions: scrub.redactions,
162340
+ total: scrub.total
162341
+ });
162342
+ }
162137
162343
  const obs = {
162138
162344
  id,
162139
162345
  projectId: attribution.projectId,
162140
162346
  sessionId: ev.sessionId,
162141
162347
  source: ev.event,
162142
162348
  category: extractCategory(ev.event, ev.payload),
162143
- payloadJson: JSON.stringify(ev.payload),
162349
+ payloadJson: scrub.sanitized,
162144
162350
  importance: ev.importance,
162145
162351
  createdAt: ev.ts,
162146
162352
  agentId: ev.agentId,
@@ -162201,6 +162407,7 @@ var init_hook_service = __esm(() => {
162201
162407
  init_observation_extractor();
162202
162408
  init_observation_consolidation_job();
162203
162409
  init_attribution_resolver();
162410
+ init_credential_scrub();
162204
162411
  ValidationError = class ValidationError extends Error {
162205
162412
  code;
162206
162413
  constructor(code, message) {
@@ -162225,7 +162432,7 @@ var init_hook_service = __esm(() => {
162225
162432
  // ../../packages/core/dist/services/bootstrap/bootstrap-service.js
162226
162433
  import { randomUUID as randomUUID9 } from "crypto";
162227
162434
  import fs13 from "fs";
162228
- import path23 from "path";
162435
+ import path25 from "path";
162229
162436
  import { spawn as spawn2 } from "child_process";
162230
162437
  function readBootstrapConfig() {
162231
162438
  try {
@@ -162385,7 +162592,7 @@ async function scanSignals(_projectId, projectRoot, caps, symbolGraph, gitRunner
162385
162592
  }
162386
162593
  try {
162387
162594
  for (const name26 of README_CANDIDATES) {
162388
- const p = path23.join(projectRoot, name26);
162595
+ const p = path25.join(projectRoot, name26);
162389
162596
  if (fs13.existsSync(p) && fs13.statSync(p).isFile()) {
162390
162597
  const buf = fs13.readFileSync(p);
162391
162598
  signals.readme = buf.slice(0, MAX_README_BYTES).toString("utf8");
@@ -162396,14 +162603,14 @@ async function scanSignals(_projectId, projectRoot, caps, symbolGraph, gitRunner
162396
162603
  logger.debug("bootstrap scan: README read failed", { error: e.message });
162397
162604
  }
162398
162605
  try {
162399
- const docsDir = path23.join(projectRoot, "docs");
162606
+ const docsDir = path25.join(projectRoot, "docs");
162400
162607
  if (fs13.existsSync(docsDir) && fs13.statSync(docsDir).isDirectory()) {
162401
162608
  const entries = walkMarkdown(docsDir).slice(0, MAX_DOCS);
162402
162609
  for (const rel of entries) {
162403
162610
  try {
162404
162611
  const buf = fs13.readFileSync(rel);
162405
162612
  signals.docs.push({
162406
- path: path23.relative(projectRoot, rel),
162613
+ path: path25.relative(projectRoot, rel),
162407
162614
  snippet: buf.slice(0, MAX_DOC_BYTES).toString("utf8")
162408
162615
  });
162409
162616
  } catch {}
@@ -162414,7 +162621,7 @@ async function scanSignals(_projectId, projectRoot, caps, symbolGraph, gitRunner
162414
162621
  }
162415
162622
  try {
162416
162623
  for (const name26 of MANIFEST_FILES) {
162417
- const p = path23.join(projectRoot, name26);
162624
+ const p = path25.join(projectRoot, name26);
162418
162625
  if (!fs13.existsSync(p) || !fs13.statSync(p).isFile())
162419
162626
  continue;
162420
162627
  const raw2 = fs13.readFileSync(p).slice(0, MAX_MANIFEST_BYTES).toString("utf8");
@@ -162462,7 +162669,7 @@ function walkMarkdown(dir) {
162462
162669
  continue;
162463
162670
  }
162464
162671
  for (const e of entries) {
162465
- const full = path23.join(cur, e.name);
162672
+ const full = path25.join(cur, e.name);
162466
162673
  if (e.isDirectory()) {
162467
162674
  if (e.name === "node_modules" || e.name.startsWith("."))
162468
162675
  continue;
@@ -165980,7 +166187,7 @@ init_dist();
165980
166187
  init_dist15();
165981
166188
  init_dist();
165982
166189
  import fs14 from "fs/promises";
165983
- import path24 from "path";
166190
+ import path26 from "path";
165984
166191
  var _indexProjectTool = null;
165985
166192
  function indexProjectTool() {
165986
166193
  if (!_indexProjectTool)
@@ -166250,7 +166457,7 @@ class EmbeddedApiClient {
166250
166457
  } else {
166251
166458
  end = start + 20;
166252
166459
  }
166253
- const absolutePath = path24.join(workspace.project_path, file2);
166460
+ const absolutePath = path26.join(workspace.project_path, file2);
166254
166461
  const content = await fs14.readFile(absolutePath, "utf-8");
166255
166462
  const lines = content.split(/\r?\n/);
166256
166463
  const slice = lines.slice(start - 1, Math.min(lines.length, end));
@@ -166474,21 +166681,21 @@ class EmbeddedApiClient {
166474
166681
  async uploadAndIndex(params) {
166475
166682
  const rawBase = params.projectId || params.projectPath.replace(/\\/g, "/").split("/").filter(Boolean).pop() || "default";
166476
166683
  const finalProjectId = rawBase.replace(/[^a-zA-Z0-9_.-]/g, "_").slice(0, 128);
166477
- const uploadRoot = process.env.MASSA_AI_UPLOAD_DIR || path24.join(getGlobalDataDir(), "uploads");
166478
- const stagingDir = path24.resolve(uploadRoot, finalProjectId);
166684
+ const uploadRoot = process.env.MASSA_AI_UPLOAD_DIR || path26.join(getGlobalDataDir(), "uploads");
166685
+ const stagingDir = path26.resolve(uploadRoot, finalProjectId);
166479
166686
  await fs14.rm(stagingDir, { recursive: true, force: true });
166480
166687
  await fs14.mkdir(stagingDir, { recursive: true });
166481
166688
  const WRITE_BATCH = 20;
166482
166689
  for (let i = 0;i < params.files.length; i += WRITE_BATCH) {
166483
166690
  await Promise.all(params.files.slice(i, i + WRITE_BATCH).map(async (file2) => {
166484
- if (path24.isAbsolute(file2.relativePath) || file2.relativePath.includes("..")) {
166691
+ if (path26.isAbsolute(file2.relativePath) || file2.relativePath.includes("..")) {
166485
166692
  throw new Error(`Invalid file path: ${file2.relativePath}`);
166486
166693
  }
166487
- const dest = path24.resolve(stagingDir, file2.relativePath.replace(/\//g, path24.sep));
166488
- if (!dest.startsWith(stagingDir + path24.sep)) {
166694
+ const dest = path26.resolve(stagingDir, file2.relativePath.replace(/\//g, path26.sep));
166695
+ if (!dest.startsWith(stagingDir + path26.sep)) {
166489
166696
  throw new Error(`Path escapes staging directory: ${file2.relativePath}`);
166490
166697
  }
166491
- await fs14.mkdir(path24.dirname(dest), { recursive: true });
166698
+ await fs14.mkdir(path26.dirname(dest), { recursive: true });
166492
166699
  await fs14.writeFile(dest, file2.content, "utf-8");
166493
166700
  }));
166494
166701
  }
@@ -166822,7 +167029,7 @@ class EmbeddedApiClient {
166822
167029
  // src/file-collector.ts
166823
167030
  init_config();
166824
167031
  import fs15 from "fs/promises";
166825
- import path25 from "path";
167032
+ import path27 from "path";
166826
167033
  var SKIP_DIRS = new Set([
166827
167034
  "node_modules",
166828
167035
  ".git",
@@ -166872,13 +167079,13 @@ async function walk2(root2, dir, files, state, allowed) {
166872
167079
  break;
166873
167080
  if (entry2.isDirectory()) {
166874
167081
  if (!SKIP_DIRS.has(entry2.name) && !entry2.name.startsWith(".")) {
166875
- await walk2(root2, path25.join(dir, entry2.name), files, state, allowed);
167082
+ await walk2(root2, path27.join(dir, entry2.name), files, state, allowed);
166876
167083
  }
166877
167084
  } else if (entry2.isFile()) {
166878
- const ext2 = path25.extname(entry2.name).toLowerCase();
167085
+ const ext2 = path27.extname(entry2.name).toLowerCase();
166879
167086
  if (!allowed.has(ext2))
166880
167087
  continue;
166881
- const fullPath = path25.join(dir, entry2.name);
167088
+ const fullPath = path27.join(dir, entry2.name);
166882
167089
  try {
166883
167090
  const stat = await fs15.stat(fullPath);
166884
167091
  if (stat.size > MAX_FILE_BYTES)
@@ -166887,7 +167094,7 @@ async function walk2(root2, dir, files, state, allowed) {
166887
167094
  continue;
166888
167095
  const content = await fs15.readFile(fullPath, "utf-8");
166889
167096
  state.totalBytes += stat.size;
166890
- const relativePath = path25.relative(root2, fullPath).split(path25.sep).join("/");
167097
+ const relativePath = path27.relative(root2, fullPath).split(path27.sep).join("/");
166891
167098
  files.push({ relativePath, content });
166892
167099
  } catch {}
166893
167100
  }