@massu/core 1.4.0-soak.0 → 1.5.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 (68) hide show
  1. package/commands/README.md +0 -3
  2. package/dist/cli.js +9423 -5453
  3. package/dist/hooks/auto-learning-pipeline.js +27 -1
  4. package/dist/hooks/classify-failure.js +27 -1
  5. package/dist/hooks/cost-tracker.js +27 -1
  6. package/dist/hooks/fix-detector.js +27 -1
  7. package/dist/hooks/incident-pipeline.js +27 -1
  8. package/dist/hooks/post-edit-context.js +27 -1
  9. package/dist/hooks/post-tool-use.js +27 -1
  10. package/dist/hooks/pre-compact.js +27 -1
  11. package/dist/hooks/pre-delete-check.js +27 -1
  12. package/dist/hooks/quality-event.js +27 -1
  13. package/dist/hooks/rule-enforcement-pipeline.js +27 -1
  14. package/dist/hooks/session-end.js +27 -1
  15. package/dist/hooks/session-start.js +2677 -2675
  16. package/dist/hooks/user-prompt.js +27 -1
  17. package/docs/AUTHORING-ADAPTERS.md +207 -0
  18. package/docs/SECURITY.md +250 -0
  19. package/package.json +10 -3
  20. package/src/adapter.ts +90 -0
  21. package/src/cli.ts +7 -0
  22. package/src/commands/adapters.ts +824 -0
  23. package/src/commands/config-check-drift.ts +1 -0
  24. package/src/commands/config-refresh.ts +4 -3
  25. package/src/commands/config-upgrade.ts +1 -0
  26. package/src/commands/doctor.ts +2 -0
  27. package/src/commands/init.ts +3 -1
  28. package/src/commands/template-engine.ts +0 -2
  29. package/src/commands/watch.ts +1 -1
  30. package/src/config.ts +71 -0
  31. package/src/detect/adapters/aspnet.ts +293 -0
  32. package/src/detect/adapters/discover.ts +469 -0
  33. package/src/detect/adapters/go-chi.ts +261 -0
  34. package/src/detect/adapters/index.ts +49 -0
  35. package/src/detect/adapters/phoenix.ts +277 -0
  36. package/src/detect/adapters/python-flask.ts +235 -0
  37. package/src/detect/adapters/rails.ts +279 -0
  38. package/src/detect/adapters/runner.ts +32 -0
  39. package/src/detect/adapters/spring.ts +284 -0
  40. package/src/detect/adapters/tree-sitter-loader.ts +171 -2
  41. package/src/detect/adapters/types.ts +19 -2
  42. package/src/detect/migrate.ts +4 -4
  43. package/src/detect/monorepo-detector.ts +1 -0
  44. package/src/hooks/post-tool-use.ts +1 -0
  45. package/src/hooks/session-start.ts +1 -0
  46. package/src/lib/fileLock.ts +203 -0
  47. package/src/lib/installLock.ts +31 -144
  48. package/src/lsp/auto-detect.ts +10 -1
  49. package/src/lsp/client.ts +188 -2
  50. package/src/memory-file-ingest.ts +1 -0
  51. package/src/security/adapter-origin.ts +130 -0
  52. package/src/security/adapter-verifier.ts +319 -0
  53. package/src/security/atomic-write.ts +164 -0
  54. package/src/security/fetcher.ts +200 -0
  55. package/src/security/install-tracking.ts +319 -0
  56. package/src/security/local-fingerprint.ts +225 -0
  57. package/src/security/manifest-cache.ts +333 -0
  58. package/src/security/manifest-schema.ts +129 -0
  59. package/src/security/registry-pubkey.generated.ts +35 -0
  60. package/src/security/telemetry.ts +320 -0
  61. package/src/watch/daemon.ts +1 -1
  62. package/src/watch/paths.ts +2 -2
  63. package/templates/aspnet/massu.config.yaml +57 -0
  64. package/templates/go-chi/massu.config.yaml +52 -0
  65. package/templates/phoenix/massu.config.yaml +54 -0
  66. package/templates/python-flask/massu.config.yaml +51 -0
  67. package/templates/rails/massu.config.yaml +56 -0
  68. package/templates/spring/massu.config.yaml +56 -0
@@ -292,11 +292,31 @@ var WatchConfigSchema = z.object({
292
292
  max_watched_files: z.number().int().positive().default(1e4),
293
293
  paths_full_root_opt_in: z.boolean().default(false)
294
294
  }).passthrough().optional();
295
+ var AdapterLocalPathSchema = z.string().refine((s) => !/^([A-Za-z]:[\\/]|[\\/])/.test(s), {
296
+ message: "absolute paths are rejected; adapters.local entries must be relative to the massu.config.yaml directory"
297
+ }).refine((s) => !s.split(/[\\/]/).includes(".."), {
298
+ message: "parent-directory traversal (`..`) is rejected; adapters.local entries must stay inside the project tree"
299
+ }).transform((s) => s.split(/[\\/]/).filter((part) => part !== "" && part !== ".").join("/"));
300
+ var AdaptersConfigSchema = z.object({
301
+ enabled: z.boolean().default(false),
302
+ local: z.array(AdapterLocalPathSchema).default([])
303
+ }).passthrough().optional();
304
+ var TelemetryConfigSchema = z.object({
305
+ adapters: z.boolean().default(false)
306
+ }).passthrough().optional();
295
307
  var LSPConfigSchema = z.object({
296
308
  enabled: z.boolean().default(false),
297
309
  servers: z.array(z.object({
298
310
  language: z.string(),
299
- command: z.string()
311
+ command: z.string(),
312
+ // F-014 (closed 2026-05-06): explicit opt-in to spawn SUID/SGID
313
+ // binaries. Default false — argv[0] with the SUID bit is rejected
314
+ // unless this is true. Decision is auditable in the YAML.
315
+ allow_setuid: z.boolean().default(false),
316
+ // F-015 (closed 2026-05-06): per-server RSS budget (MB). Watchdog
317
+ // SIGKILLs the server after sustained breach. Default 1024 MB.
318
+ // Set to 0 to disable the watchdog for this server.
319
+ max_rss_mb: z.number().int().nonnegative().default(1024)
300
320
  })).default([]),
301
321
  autoDetect: z.object({
302
322
  viaPortScan: z.boolean().default(false)
@@ -339,6 +359,10 @@ var RawConfigSchema = z.object({
339
359
  detected: DetectedConfigSchema,
340
360
  // Plan 3a: file-watcher daemon tunables
341
361
  watch: WatchConfigSchema,
362
+ // Plan 3c: third-party adapter registry kill-switch + signing override + local-path opt-in.
363
+ adapters: AdaptersConfigSchema,
364
+ // Plan 3c: anonymous adapter-discovery telemetry opt-in (default off).
365
+ telemetry: TelemetryConfigSchema,
342
366
  // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
343
367
  lsp: LSPConfigSchema.optional()
344
368
  }).passthrough();
@@ -450,6 +474,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
450
474
  detection: parsed.detection,
451
475
  detected: parsed.detected,
452
476
  watch: parsed.watch,
477
+ adapters: parsed.adapters,
478
+ telemetry: parsed.telemetry,
453
479
  lsp: parsed.lsp
454
480
  };
455
481
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
@@ -292,11 +292,31 @@ var WatchConfigSchema = z.object({
292
292
  max_watched_files: z.number().int().positive().default(1e4),
293
293
  paths_full_root_opt_in: z.boolean().default(false)
294
294
  }).passthrough().optional();
295
+ var AdapterLocalPathSchema = z.string().refine((s) => !/^([A-Za-z]:[\\/]|[\\/])/.test(s), {
296
+ message: "absolute paths are rejected; adapters.local entries must be relative to the massu.config.yaml directory"
297
+ }).refine((s) => !s.split(/[\\/]/).includes(".."), {
298
+ message: "parent-directory traversal (`..`) is rejected; adapters.local entries must stay inside the project tree"
299
+ }).transform((s) => s.split(/[\\/]/).filter((part) => part !== "" && part !== ".").join("/"));
300
+ var AdaptersConfigSchema = z.object({
301
+ enabled: z.boolean().default(false),
302
+ local: z.array(AdapterLocalPathSchema).default([])
303
+ }).passthrough().optional();
304
+ var TelemetryConfigSchema = z.object({
305
+ adapters: z.boolean().default(false)
306
+ }).passthrough().optional();
295
307
  var LSPConfigSchema = z.object({
296
308
  enabled: z.boolean().default(false),
297
309
  servers: z.array(z.object({
298
310
  language: z.string(),
299
- command: z.string()
311
+ command: z.string(),
312
+ // F-014 (closed 2026-05-06): explicit opt-in to spawn SUID/SGID
313
+ // binaries. Default false — argv[0] with the SUID bit is rejected
314
+ // unless this is true. Decision is auditable in the YAML.
315
+ allow_setuid: z.boolean().default(false),
316
+ // F-015 (closed 2026-05-06): per-server RSS budget (MB). Watchdog
317
+ // SIGKILLs the server after sustained breach. Default 1024 MB.
318
+ // Set to 0 to disable the watchdog for this server.
319
+ max_rss_mb: z.number().int().nonnegative().default(1024)
300
320
  })).default([]),
301
321
  autoDetect: z.object({
302
322
  viaPortScan: z.boolean().default(false)
@@ -339,6 +359,10 @@ var RawConfigSchema = z.object({
339
359
  detected: DetectedConfigSchema,
340
360
  // Plan 3a: file-watcher daemon tunables
341
361
  watch: WatchConfigSchema,
362
+ // Plan 3c: third-party adapter registry kill-switch + signing override + local-path opt-in.
363
+ adapters: AdaptersConfigSchema,
364
+ // Plan 3c: anonymous adapter-discovery telemetry opt-in (default off).
365
+ telemetry: TelemetryConfigSchema,
342
366
  // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
343
367
  lsp: LSPConfigSchema.optional()
344
368
  }).passthrough();
@@ -450,6 +474,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
450
474
  detection: parsed.detection,
451
475
  detected: parsed.detected,
452
476
  watch: parsed.watch,
477
+ adapters: parsed.adapters,
478
+ telemetry: parsed.telemetry,
453
479
  lsp: parsed.lsp
454
480
  };
455
481
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
@@ -292,11 +292,31 @@ var WatchConfigSchema = z.object({
292
292
  max_watched_files: z.number().int().positive().default(1e4),
293
293
  paths_full_root_opt_in: z.boolean().default(false)
294
294
  }).passthrough().optional();
295
+ var AdapterLocalPathSchema = z.string().refine((s) => !/^([A-Za-z]:[\\/]|[\\/])/.test(s), {
296
+ message: "absolute paths are rejected; adapters.local entries must be relative to the massu.config.yaml directory"
297
+ }).refine((s) => !s.split(/[\\/]/).includes(".."), {
298
+ message: "parent-directory traversal (`..`) is rejected; adapters.local entries must stay inside the project tree"
299
+ }).transform((s) => s.split(/[\\/]/).filter((part) => part !== "" && part !== ".").join("/"));
300
+ var AdaptersConfigSchema = z.object({
301
+ enabled: z.boolean().default(false),
302
+ local: z.array(AdapterLocalPathSchema).default([])
303
+ }).passthrough().optional();
304
+ var TelemetryConfigSchema = z.object({
305
+ adapters: z.boolean().default(false)
306
+ }).passthrough().optional();
295
307
  var LSPConfigSchema = z.object({
296
308
  enabled: z.boolean().default(false),
297
309
  servers: z.array(z.object({
298
310
  language: z.string(),
299
- command: z.string()
311
+ command: z.string(),
312
+ // F-014 (closed 2026-05-06): explicit opt-in to spawn SUID/SGID
313
+ // binaries. Default false — argv[0] with the SUID bit is rejected
314
+ // unless this is true. Decision is auditable in the YAML.
315
+ allow_setuid: z.boolean().default(false),
316
+ // F-015 (closed 2026-05-06): per-server RSS budget (MB). Watchdog
317
+ // SIGKILLs the server after sustained breach. Default 1024 MB.
318
+ // Set to 0 to disable the watchdog for this server.
319
+ max_rss_mb: z.number().int().nonnegative().default(1024)
300
320
  })).default([]),
301
321
  autoDetect: z.object({
302
322
  viaPortScan: z.boolean().default(false)
@@ -339,6 +359,10 @@ var RawConfigSchema = z.object({
339
359
  detected: DetectedConfigSchema,
340
360
  // Plan 3a: file-watcher daemon tunables
341
361
  watch: WatchConfigSchema,
362
+ // Plan 3c: third-party adapter registry kill-switch + signing override + local-path opt-in.
363
+ adapters: AdaptersConfigSchema,
364
+ // Plan 3c: anonymous adapter-discovery telemetry opt-in (default off).
365
+ telemetry: TelemetryConfigSchema,
342
366
  // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
343
367
  lsp: LSPConfigSchema.optional()
344
368
  }).passthrough();
@@ -450,6 +474,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
450
474
  detection: parsed.detection,
451
475
  detected: parsed.detected,
452
476
  watch: parsed.watch,
477
+ adapters: parsed.adapters,
478
+ telemetry: parsed.telemetry,
453
479
  lsp: parsed.lsp
454
480
  };
455
481
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
@@ -292,11 +292,31 @@ var WatchConfigSchema = z.object({
292
292
  max_watched_files: z.number().int().positive().default(1e4),
293
293
  paths_full_root_opt_in: z.boolean().default(false)
294
294
  }).passthrough().optional();
295
+ var AdapterLocalPathSchema = z.string().refine((s) => !/^([A-Za-z]:[\\/]|[\\/])/.test(s), {
296
+ message: "absolute paths are rejected; adapters.local entries must be relative to the massu.config.yaml directory"
297
+ }).refine((s) => !s.split(/[\\/]/).includes(".."), {
298
+ message: "parent-directory traversal (`..`) is rejected; adapters.local entries must stay inside the project tree"
299
+ }).transform((s) => s.split(/[\\/]/).filter((part) => part !== "" && part !== ".").join("/"));
300
+ var AdaptersConfigSchema = z.object({
301
+ enabled: z.boolean().default(false),
302
+ local: z.array(AdapterLocalPathSchema).default([])
303
+ }).passthrough().optional();
304
+ var TelemetryConfigSchema = z.object({
305
+ adapters: z.boolean().default(false)
306
+ }).passthrough().optional();
295
307
  var LSPConfigSchema = z.object({
296
308
  enabled: z.boolean().default(false),
297
309
  servers: z.array(z.object({
298
310
  language: z.string(),
299
- command: z.string()
311
+ command: z.string(),
312
+ // F-014 (closed 2026-05-06): explicit opt-in to spawn SUID/SGID
313
+ // binaries. Default false — argv[0] with the SUID bit is rejected
314
+ // unless this is true. Decision is auditable in the YAML.
315
+ allow_setuid: z.boolean().default(false),
316
+ // F-015 (closed 2026-05-06): per-server RSS budget (MB). Watchdog
317
+ // SIGKILLs the server after sustained breach. Default 1024 MB.
318
+ // Set to 0 to disable the watchdog for this server.
319
+ max_rss_mb: z.number().int().nonnegative().default(1024)
300
320
  })).default([]),
301
321
  autoDetect: z.object({
302
322
  viaPortScan: z.boolean().default(false)
@@ -339,6 +359,10 @@ var RawConfigSchema = z.object({
339
359
  detected: DetectedConfigSchema,
340
360
  // Plan 3a: file-watcher daemon tunables
341
361
  watch: WatchConfigSchema,
362
+ // Plan 3c: third-party adapter registry kill-switch + signing override + local-path opt-in.
363
+ adapters: AdaptersConfigSchema,
364
+ // Plan 3c: anonymous adapter-discovery telemetry opt-in (default off).
365
+ telemetry: TelemetryConfigSchema,
342
366
  // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
343
367
  lsp: LSPConfigSchema.optional()
344
368
  }).passthrough();
@@ -450,6 +474,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
450
474
  detection: parsed.detection,
451
475
  detected: parsed.detected,
452
476
  watch: parsed.watch,
477
+ adapters: parsed.adapters,
478
+ telemetry: parsed.telemetry,
453
479
  lsp: parsed.lsp
454
480
  };
455
481
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
@@ -291,11 +291,31 @@ var WatchConfigSchema = z.object({
291
291
  max_watched_files: z.number().int().positive().default(1e4),
292
292
  paths_full_root_opt_in: z.boolean().default(false)
293
293
  }).passthrough().optional();
294
+ var AdapterLocalPathSchema = z.string().refine((s) => !/^([A-Za-z]:[\\/]|[\\/])/.test(s), {
295
+ message: "absolute paths are rejected; adapters.local entries must be relative to the massu.config.yaml directory"
296
+ }).refine((s) => !s.split(/[\\/]/).includes(".."), {
297
+ message: "parent-directory traversal (`..`) is rejected; adapters.local entries must stay inside the project tree"
298
+ }).transform((s) => s.split(/[\\/]/).filter((part) => part !== "" && part !== ".").join("/"));
299
+ var AdaptersConfigSchema = z.object({
300
+ enabled: z.boolean().default(false),
301
+ local: z.array(AdapterLocalPathSchema).default([])
302
+ }).passthrough().optional();
303
+ var TelemetryConfigSchema = z.object({
304
+ adapters: z.boolean().default(false)
305
+ }).passthrough().optional();
294
306
  var LSPConfigSchema = z.object({
295
307
  enabled: z.boolean().default(false),
296
308
  servers: z.array(z.object({
297
309
  language: z.string(),
298
- command: z.string()
310
+ command: z.string(),
311
+ // F-014 (closed 2026-05-06): explicit opt-in to spawn SUID/SGID
312
+ // binaries. Default false — argv[0] with the SUID bit is rejected
313
+ // unless this is true. Decision is auditable in the YAML.
314
+ allow_setuid: z.boolean().default(false),
315
+ // F-015 (closed 2026-05-06): per-server RSS budget (MB). Watchdog
316
+ // SIGKILLs the server after sustained breach. Default 1024 MB.
317
+ // Set to 0 to disable the watchdog for this server.
318
+ max_rss_mb: z.number().int().nonnegative().default(1024)
299
319
  })).default([]),
300
320
  autoDetect: z.object({
301
321
  viaPortScan: z.boolean().default(false)
@@ -338,6 +358,10 @@ var RawConfigSchema = z.object({
338
358
  detected: DetectedConfigSchema,
339
359
  // Plan 3a: file-watcher daemon tunables
340
360
  watch: WatchConfigSchema,
361
+ // Plan 3c: third-party adapter registry kill-switch + signing override + local-path opt-in.
362
+ adapters: AdaptersConfigSchema,
363
+ // Plan 3c: anonymous adapter-discovery telemetry opt-in (default off).
364
+ telemetry: TelemetryConfigSchema,
341
365
  // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
342
366
  lsp: LSPConfigSchema.optional()
343
367
  }).passthrough();
@@ -449,6 +473,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
449
473
  detection: parsed.detection,
450
474
  detected: parsed.detected,
451
475
  watch: parsed.watch,
476
+ adapters: parsed.adapters,
477
+ telemetry: parsed.telemetry,
452
478
  lsp: parsed.lsp
453
479
  };
454
480
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
@@ -290,11 +290,31 @@ var WatchConfigSchema = z.object({
290
290
  max_watched_files: z.number().int().positive().default(1e4),
291
291
  paths_full_root_opt_in: z.boolean().default(false)
292
292
  }).passthrough().optional();
293
+ var AdapterLocalPathSchema = z.string().refine((s) => !/^([A-Za-z]:[\\/]|[\\/])/.test(s), {
294
+ message: "absolute paths are rejected; adapters.local entries must be relative to the massu.config.yaml directory"
295
+ }).refine((s) => !s.split(/[\\/]/).includes(".."), {
296
+ message: "parent-directory traversal (`..`) is rejected; adapters.local entries must stay inside the project tree"
297
+ }).transform((s) => s.split(/[\\/]/).filter((part) => part !== "" && part !== ".").join("/"));
298
+ var AdaptersConfigSchema = z.object({
299
+ enabled: z.boolean().default(false),
300
+ local: z.array(AdapterLocalPathSchema).default([])
301
+ }).passthrough().optional();
302
+ var TelemetryConfigSchema = z.object({
303
+ adapters: z.boolean().default(false)
304
+ }).passthrough().optional();
293
305
  var LSPConfigSchema = z.object({
294
306
  enabled: z.boolean().default(false),
295
307
  servers: z.array(z.object({
296
308
  language: z.string(),
297
- command: z.string()
309
+ command: z.string(),
310
+ // F-014 (closed 2026-05-06): explicit opt-in to spawn SUID/SGID
311
+ // binaries. Default false — argv[0] with the SUID bit is rejected
312
+ // unless this is true. Decision is auditable in the YAML.
313
+ allow_setuid: z.boolean().default(false),
314
+ // F-015 (closed 2026-05-06): per-server RSS budget (MB). Watchdog
315
+ // SIGKILLs the server after sustained breach. Default 1024 MB.
316
+ // Set to 0 to disable the watchdog for this server.
317
+ max_rss_mb: z.number().int().nonnegative().default(1024)
298
318
  })).default([]),
299
319
  autoDetect: z.object({
300
320
  viaPortScan: z.boolean().default(false)
@@ -337,6 +357,10 @@ var RawConfigSchema = z.object({
337
357
  detected: DetectedConfigSchema,
338
358
  // Plan 3a: file-watcher daemon tunables
339
359
  watch: WatchConfigSchema,
360
+ // Plan 3c: third-party adapter registry kill-switch + signing override + local-path opt-in.
361
+ adapters: AdaptersConfigSchema,
362
+ // Plan 3c: anonymous adapter-discovery telemetry opt-in (default off).
363
+ telemetry: TelemetryConfigSchema,
340
364
  // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
341
365
  lsp: LSPConfigSchema.optional()
342
366
  }).passthrough();
@@ -448,6 +472,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
448
472
  detection: parsed.detection,
449
473
  detected: parsed.detected,
450
474
  watch: parsed.watch,
475
+ adapters: parsed.adapters,
476
+ telemetry: parsed.telemetry,
451
477
  lsp: parsed.lsp
452
478
  };
453
479
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
@@ -292,11 +292,31 @@ var WatchConfigSchema = z.object({
292
292
  max_watched_files: z.number().int().positive().default(1e4),
293
293
  paths_full_root_opt_in: z.boolean().default(false)
294
294
  }).passthrough().optional();
295
+ var AdapterLocalPathSchema = z.string().refine((s) => !/^([A-Za-z]:[\\/]|[\\/])/.test(s), {
296
+ message: "absolute paths are rejected; adapters.local entries must be relative to the massu.config.yaml directory"
297
+ }).refine((s) => !s.split(/[\\/]/).includes(".."), {
298
+ message: "parent-directory traversal (`..`) is rejected; adapters.local entries must stay inside the project tree"
299
+ }).transform((s) => s.split(/[\\/]/).filter((part) => part !== "" && part !== ".").join("/"));
300
+ var AdaptersConfigSchema = z.object({
301
+ enabled: z.boolean().default(false),
302
+ local: z.array(AdapterLocalPathSchema).default([])
303
+ }).passthrough().optional();
304
+ var TelemetryConfigSchema = z.object({
305
+ adapters: z.boolean().default(false)
306
+ }).passthrough().optional();
295
307
  var LSPConfigSchema = z.object({
296
308
  enabled: z.boolean().default(false),
297
309
  servers: z.array(z.object({
298
310
  language: z.string(),
299
- command: z.string()
311
+ command: z.string(),
312
+ // F-014 (closed 2026-05-06): explicit opt-in to spawn SUID/SGID
313
+ // binaries. Default false — argv[0] with the SUID bit is rejected
314
+ // unless this is true. Decision is auditable in the YAML.
315
+ allow_setuid: z.boolean().default(false),
316
+ // F-015 (closed 2026-05-06): per-server RSS budget (MB). Watchdog
317
+ // SIGKILLs the server after sustained breach. Default 1024 MB.
318
+ // Set to 0 to disable the watchdog for this server.
319
+ max_rss_mb: z.number().int().nonnegative().default(1024)
300
320
  })).default([]),
301
321
  autoDetect: z.object({
302
322
  viaPortScan: z.boolean().default(false)
@@ -339,6 +359,10 @@ var RawConfigSchema = z.object({
339
359
  detected: DetectedConfigSchema,
340
360
  // Plan 3a: file-watcher daemon tunables
341
361
  watch: WatchConfigSchema,
362
+ // Plan 3c: third-party adapter registry kill-switch + signing override + local-path opt-in.
363
+ adapters: AdaptersConfigSchema,
364
+ // Plan 3c: anonymous adapter-discovery telemetry opt-in (default off).
365
+ telemetry: TelemetryConfigSchema,
342
366
  // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
343
367
  lsp: LSPConfigSchema.optional()
344
368
  }).passthrough();
@@ -450,6 +474,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
450
474
  detection: parsed.detection,
451
475
  detected: parsed.detected,
452
476
  watch: parsed.watch,
477
+ adapters: parsed.adapters,
478
+ telemetry: parsed.telemetry,
453
479
  lsp: parsed.lsp
454
480
  };
455
481
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
@@ -292,11 +292,31 @@ var WatchConfigSchema = z.object({
292
292
  max_watched_files: z.number().int().positive().default(1e4),
293
293
  paths_full_root_opt_in: z.boolean().default(false)
294
294
  }).passthrough().optional();
295
+ var AdapterLocalPathSchema = z.string().refine((s) => !/^([A-Za-z]:[\\/]|[\\/])/.test(s), {
296
+ message: "absolute paths are rejected; adapters.local entries must be relative to the massu.config.yaml directory"
297
+ }).refine((s) => !s.split(/[\\/]/).includes(".."), {
298
+ message: "parent-directory traversal (`..`) is rejected; adapters.local entries must stay inside the project tree"
299
+ }).transform((s) => s.split(/[\\/]/).filter((part) => part !== "" && part !== ".").join("/"));
300
+ var AdaptersConfigSchema = z.object({
301
+ enabled: z.boolean().default(false),
302
+ local: z.array(AdapterLocalPathSchema).default([])
303
+ }).passthrough().optional();
304
+ var TelemetryConfigSchema = z.object({
305
+ adapters: z.boolean().default(false)
306
+ }).passthrough().optional();
295
307
  var LSPConfigSchema = z.object({
296
308
  enabled: z.boolean().default(false),
297
309
  servers: z.array(z.object({
298
310
  language: z.string(),
299
- command: z.string()
311
+ command: z.string(),
312
+ // F-014 (closed 2026-05-06): explicit opt-in to spawn SUID/SGID
313
+ // binaries. Default false — argv[0] with the SUID bit is rejected
314
+ // unless this is true. Decision is auditable in the YAML.
315
+ allow_setuid: z.boolean().default(false),
316
+ // F-015 (closed 2026-05-06): per-server RSS budget (MB). Watchdog
317
+ // SIGKILLs the server after sustained breach. Default 1024 MB.
318
+ // Set to 0 to disable the watchdog for this server.
319
+ max_rss_mb: z.number().int().nonnegative().default(1024)
300
320
  })).default([]),
301
321
  autoDetect: z.object({
302
322
  viaPortScan: z.boolean().default(false)
@@ -339,6 +359,10 @@ var RawConfigSchema = z.object({
339
359
  detected: DetectedConfigSchema,
340
360
  // Plan 3a: file-watcher daemon tunables
341
361
  watch: WatchConfigSchema,
362
+ // Plan 3c: third-party adapter registry kill-switch + signing override + local-path opt-in.
363
+ adapters: AdaptersConfigSchema,
364
+ // Plan 3c: anonymous adapter-discovery telemetry opt-in (default off).
365
+ telemetry: TelemetryConfigSchema,
342
366
  // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
343
367
  lsp: LSPConfigSchema.optional()
344
368
  }).passthrough();
@@ -450,6 +474,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
450
474
  detection: parsed.detection,
451
475
  detected: parsed.detected,
452
476
  watch: parsed.watch,
477
+ adapters: parsed.adapters,
478
+ telemetry: parsed.telemetry,
453
479
  lsp: parsed.lsp
454
480
  };
455
481
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
@@ -291,11 +291,31 @@ var WatchConfigSchema = z.object({
291
291
  max_watched_files: z.number().int().positive().default(1e4),
292
292
  paths_full_root_opt_in: z.boolean().default(false)
293
293
  }).passthrough().optional();
294
+ var AdapterLocalPathSchema = z.string().refine((s) => !/^([A-Za-z]:[\\/]|[\\/])/.test(s), {
295
+ message: "absolute paths are rejected; adapters.local entries must be relative to the massu.config.yaml directory"
296
+ }).refine((s) => !s.split(/[\\/]/).includes(".."), {
297
+ message: "parent-directory traversal (`..`) is rejected; adapters.local entries must stay inside the project tree"
298
+ }).transform((s) => s.split(/[\\/]/).filter((part) => part !== "" && part !== ".").join("/"));
299
+ var AdaptersConfigSchema = z.object({
300
+ enabled: z.boolean().default(false),
301
+ local: z.array(AdapterLocalPathSchema).default([])
302
+ }).passthrough().optional();
303
+ var TelemetryConfigSchema = z.object({
304
+ adapters: z.boolean().default(false)
305
+ }).passthrough().optional();
294
306
  var LSPConfigSchema = z.object({
295
307
  enabled: z.boolean().default(false),
296
308
  servers: z.array(z.object({
297
309
  language: z.string(),
298
- command: z.string()
310
+ command: z.string(),
311
+ // F-014 (closed 2026-05-06): explicit opt-in to spawn SUID/SGID
312
+ // binaries. Default false — argv[0] with the SUID bit is rejected
313
+ // unless this is true. Decision is auditable in the YAML.
314
+ allow_setuid: z.boolean().default(false),
315
+ // F-015 (closed 2026-05-06): per-server RSS budget (MB). Watchdog
316
+ // SIGKILLs the server after sustained breach. Default 1024 MB.
317
+ // Set to 0 to disable the watchdog for this server.
318
+ max_rss_mb: z.number().int().nonnegative().default(1024)
299
319
  })).default([]),
300
320
  autoDetect: z.object({
301
321
  viaPortScan: z.boolean().default(false)
@@ -338,6 +358,10 @@ var RawConfigSchema = z.object({
338
358
  detected: DetectedConfigSchema,
339
359
  // Plan 3a: file-watcher daemon tunables
340
360
  watch: WatchConfigSchema,
361
+ // Plan 3c: third-party adapter registry kill-switch + signing override + local-path opt-in.
362
+ adapters: AdaptersConfigSchema,
363
+ // Plan 3c: anonymous adapter-discovery telemetry opt-in (default off).
364
+ telemetry: TelemetryConfigSchema,
341
365
  // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
342
366
  lsp: LSPConfigSchema.optional()
343
367
  }).passthrough();
@@ -449,6 +473,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
449
473
  detection: parsed.detection,
450
474
  detected: parsed.detected,
451
475
  watch: parsed.watch,
476
+ adapters: parsed.adapters,
477
+ telemetry: parsed.telemetry,
452
478
  lsp: parsed.lsp
453
479
  };
454
480
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
@@ -292,11 +292,31 @@ var WatchConfigSchema = z.object({
292
292
  max_watched_files: z.number().int().positive().default(1e4),
293
293
  paths_full_root_opt_in: z.boolean().default(false)
294
294
  }).passthrough().optional();
295
+ var AdapterLocalPathSchema = z.string().refine((s) => !/^([A-Za-z]:[\\/]|[\\/])/.test(s), {
296
+ message: "absolute paths are rejected; adapters.local entries must be relative to the massu.config.yaml directory"
297
+ }).refine((s) => !s.split(/[\\/]/).includes(".."), {
298
+ message: "parent-directory traversal (`..`) is rejected; adapters.local entries must stay inside the project tree"
299
+ }).transform((s) => s.split(/[\\/]/).filter((part) => part !== "" && part !== ".").join("/"));
300
+ var AdaptersConfigSchema = z.object({
301
+ enabled: z.boolean().default(false),
302
+ local: z.array(AdapterLocalPathSchema).default([])
303
+ }).passthrough().optional();
304
+ var TelemetryConfigSchema = z.object({
305
+ adapters: z.boolean().default(false)
306
+ }).passthrough().optional();
295
307
  var LSPConfigSchema = z.object({
296
308
  enabled: z.boolean().default(false),
297
309
  servers: z.array(z.object({
298
310
  language: z.string(),
299
- command: z.string()
311
+ command: z.string(),
312
+ // F-014 (closed 2026-05-06): explicit opt-in to spawn SUID/SGID
313
+ // binaries. Default false — argv[0] with the SUID bit is rejected
314
+ // unless this is true. Decision is auditable in the YAML.
315
+ allow_setuid: z.boolean().default(false),
316
+ // F-015 (closed 2026-05-06): per-server RSS budget (MB). Watchdog
317
+ // SIGKILLs the server after sustained breach. Default 1024 MB.
318
+ // Set to 0 to disable the watchdog for this server.
319
+ max_rss_mb: z.number().int().nonnegative().default(1024)
300
320
  })).default([]),
301
321
  autoDetect: z.object({
302
322
  viaPortScan: z.boolean().default(false)
@@ -339,6 +359,10 @@ var RawConfigSchema = z.object({
339
359
  detected: DetectedConfigSchema,
340
360
  // Plan 3a: file-watcher daemon tunables
341
361
  watch: WatchConfigSchema,
362
+ // Plan 3c: third-party adapter registry kill-switch + signing override + local-path opt-in.
363
+ adapters: AdaptersConfigSchema,
364
+ // Plan 3c: anonymous adapter-discovery telemetry opt-in (default off).
365
+ telemetry: TelemetryConfigSchema,
342
366
  // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
343
367
  lsp: LSPConfigSchema.optional()
344
368
  }).passthrough();
@@ -450,6 +474,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
450
474
  detection: parsed.detection,
451
475
  detected: parsed.detected,
452
476
  watch: parsed.watch,
477
+ adapters: parsed.adapters,
478
+ telemetry: parsed.telemetry,
453
479
  lsp: parsed.lsp
454
480
  };
455
481
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
@@ -290,11 +290,31 @@ var WatchConfigSchema = z.object({
290
290
  max_watched_files: z.number().int().positive().default(1e4),
291
291
  paths_full_root_opt_in: z.boolean().default(false)
292
292
  }).passthrough().optional();
293
+ var AdapterLocalPathSchema = z.string().refine((s) => !/^([A-Za-z]:[\\/]|[\\/])/.test(s), {
294
+ message: "absolute paths are rejected; adapters.local entries must be relative to the massu.config.yaml directory"
295
+ }).refine((s) => !s.split(/[\\/]/).includes(".."), {
296
+ message: "parent-directory traversal (`..`) is rejected; adapters.local entries must stay inside the project tree"
297
+ }).transform((s) => s.split(/[\\/]/).filter((part) => part !== "" && part !== ".").join("/"));
298
+ var AdaptersConfigSchema = z.object({
299
+ enabled: z.boolean().default(false),
300
+ local: z.array(AdapterLocalPathSchema).default([])
301
+ }).passthrough().optional();
302
+ var TelemetryConfigSchema = z.object({
303
+ adapters: z.boolean().default(false)
304
+ }).passthrough().optional();
293
305
  var LSPConfigSchema = z.object({
294
306
  enabled: z.boolean().default(false),
295
307
  servers: z.array(z.object({
296
308
  language: z.string(),
297
- command: z.string()
309
+ command: z.string(),
310
+ // F-014 (closed 2026-05-06): explicit opt-in to spawn SUID/SGID
311
+ // binaries. Default false — argv[0] with the SUID bit is rejected
312
+ // unless this is true. Decision is auditable in the YAML.
313
+ allow_setuid: z.boolean().default(false),
314
+ // F-015 (closed 2026-05-06): per-server RSS budget (MB). Watchdog
315
+ // SIGKILLs the server after sustained breach. Default 1024 MB.
316
+ // Set to 0 to disable the watchdog for this server.
317
+ max_rss_mb: z.number().int().nonnegative().default(1024)
298
318
  })).default([]),
299
319
  autoDetect: z.object({
300
320
  viaPortScan: z.boolean().default(false)
@@ -337,6 +357,10 @@ var RawConfigSchema = z.object({
337
357
  detected: DetectedConfigSchema,
338
358
  // Plan 3a: file-watcher daemon tunables
339
359
  watch: WatchConfigSchema,
360
+ // Plan 3c: third-party adapter registry kill-switch + signing override + local-path opt-in.
361
+ adapters: AdaptersConfigSchema,
362
+ // Plan 3c: anonymous adapter-discovery telemetry opt-in (default off).
363
+ telemetry: TelemetryConfigSchema,
340
364
  // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
341
365
  lsp: LSPConfigSchema.optional()
342
366
  }).passthrough();
@@ -448,6 +472,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
448
472
  detection: parsed.detection,
449
473
  detected: parsed.detected,
450
474
  watch: parsed.watch,
475
+ adapters: parsed.adapters,
476
+ telemetry: parsed.telemetry,
451
477
  lsp: parsed.lsp
452
478
  };
453
479
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
@@ -292,11 +292,31 @@ var WatchConfigSchema = z.object({
292
292
  max_watched_files: z.number().int().positive().default(1e4),
293
293
  paths_full_root_opt_in: z.boolean().default(false)
294
294
  }).passthrough().optional();
295
+ var AdapterLocalPathSchema = z.string().refine((s) => !/^([A-Za-z]:[\\/]|[\\/])/.test(s), {
296
+ message: "absolute paths are rejected; adapters.local entries must be relative to the massu.config.yaml directory"
297
+ }).refine((s) => !s.split(/[\\/]/).includes(".."), {
298
+ message: "parent-directory traversal (`..`) is rejected; adapters.local entries must stay inside the project tree"
299
+ }).transform((s) => s.split(/[\\/]/).filter((part) => part !== "" && part !== ".").join("/"));
300
+ var AdaptersConfigSchema = z.object({
301
+ enabled: z.boolean().default(false),
302
+ local: z.array(AdapterLocalPathSchema).default([])
303
+ }).passthrough().optional();
304
+ var TelemetryConfigSchema = z.object({
305
+ adapters: z.boolean().default(false)
306
+ }).passthrough().optional();
295
307
  var LSPConfigSchema = z.object({
296
308
  enabled: z.boolean().default(false),
297
309
  servers: z.array(z.object({
298
310
  language: z.string(),
299
- command: z.string()
311
+ command: z.string(),
312
+ // F-014 (closed 2026-05-06): explicit opt-in to spawn SUID/SGID
313
+ // binaries. Default false — argv[0] with the SUID bit is rejected
314
+ // unless this is true. Decision is auditable in the YAML.
315
+ allow_setuid: z.boolean().default(false),
316
+ // F-015 (closed 2026-05-06): per-server RSS budget (MB). Watchdog
317
+ // SIGKILLs the server after sustained breach. Default 1024 MB.
318
+ // Set to 0 to disable the watchdog for this server.
319
+ max_rss_mb: z.number().int().nonnegative().default(1024)
300
320
  })).default([]),
301
321
  autoDetect: z.object({
302
322
  viaPortScan: z.boolean().default(false)
@@ -339,6 +359,10 @@ var RawConfigSchema = z.object({
339
359
  detected: DetectedConfigSchema,
340
360
  // Plan 3a: file-watcher daemon tunables
341
361
  watch: WatchConfigSchema,
362
+ // Plan 3c: third-party adapter registry kill-switch + signing override + local-path opt-in.
363
+ adapters: AdaptersConfigSchema,
364
+ // Plan 3c: anonymous adapter-discovery telemetry opt-in (default off).
365
+ telemetry: TelemetryConfigSchema,
342
366
  // Plan 3b Phase 4: optional LSP enrichment of AST adapter results.
343
367
  lsp: LSPConfigSchema.optional()
344
368
  }).passthrough();
@@ -450,6 +474,8 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
450
474
  detection: parsed.detection,
451
475
  detected: parsed.detected,
452
476
  watch: parsed.watch,
477
+ adapters: parsed.adapters,
478
+ telemetry: parsed.telemetry,
453
479
  lsp: parsed.lsp
454
480
  };
455
481
  if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {