fraim 2.0.210 → 2.0.211

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.
@@ -385,13 +385,11 @@ function buildLearningContextSection(workspaceRoot, userId, forJob, domain) {
385
385
  coach: { ft: 'manager-coaching', gated: false, label: '(manager-facing; all entries)' },
386
386
  validated: { ft: 'validated-patterns', gated: true, label: '(entries above score threshold)' },
387
387
  };
388
- // Domain axis (issue #806, Option 1): `fraim_connect` (forJob=false) loads only
389
- // the interaction-preference files + the L0 nudge, since it runs with no job.
390
- // A job (forJob=true) loads every category globally, plus the current job's
391
- // domain files. Category order within a tier is preserved from prior behavior;
392
- // domain files are appended after the global files.
393
- const l2Cats = forJob ? ['mistake', 'pref', 'coach', 'validated'] : ['pref'];
394
- const l1Cats = forJob ? ['pref', 'coach', 'mistake', 'validated'] : ['pref'];
388
+ // Domain axis (issue #806): startup and job contexts both include the global
389
+ // learning set. Job contexts may additionally append the current job's domain
390
+ // files below the global files.
391
+ const l2Cats = ['mistake', 'pref', 'coach', 'validated'];
392
+ const l1Cats = ['pref', 'coach', 'mistake', 'validated'];
395
393
  const activeDomain = forJob && domain ? domain : null;
396
394
  const dormantOf = (meta, filePath) => meta.gated ? scanMistakePatternFile(filePath, threshold, meta.ft).dormant : 0;
397
395
  // Resolve one tier (L2 org or L1 personal) into ordered global + domain files.
@@ -4,7 +4,7 @@ exports.EmailService = void 0;
4
4
  const resend_1 = require("resend");
5
5
  class EmailService {
6
6
  constructor() {
7
- const apiKey = process.env.RESEND_API_KEY;
7
+ const apiKey = process.env.RESEND_API_KEY || (process.env.FRAIM_SUPPRESS_EMAILS === 'true' ? 'test-resend-key' : undefined);
8
8
  if (!apiKey) {
9
9
  throw new Error('RESEND_API_KEY environment variable is required');
10
10
  }
@@ -13,16 +13,20 @@ class EmailService {
13
13
  this.fromEmail = process.env.RESEND_FROM_EMAIL || 'FRAIM <onboarding@resend.dev>';
14
14
  this.baseUrl = (process.env.BASE_URL || 'https://fraimworks.ai').replace(/\/$/, '');
15
15
  }
16
+ static shouldSuppressEmail() {
17
+ return process.env.FRAIM_SUPPRESS_EMAILS === 'true'
18
+ || (process.env.NODE_ENV === 'test' && process.env.ALLOW_REAL_EMAILS_IN_TEST !== 'true');
19
+ }
16
20
  /** Wrapper around resend.emails.send — no-ops in test mode to avoid burning real API quota. */
17
21
  async sendEmail(payload) {
18
- if (process.env.NODE_ENV === 'test') {
22
+ if (EmailService.shouldSuppressEmail()) {
19
23
  console.log(`[TEST] Email suppressed — would send to ${Array.isArray(payload.to) ? payload.to.join(', ') : payload.to}: "${payload.subject}"`);
20
24
  return { data: { id: 'test-suppressed' }, error: null, headers: null };
21
25
  }
22
26
  return this.sendWithResend(payload);
23
27
  }
24
28
  async sendWithResend(payload) {
25
- if (process.env.NODE_ENV === 'test' && process.env.ALLOW_REAL_EMAILS_IN_TEST !== 'true') {
29
+ if (EmailService.shouldSuppressEmail()) {
26
30
  console.log(`[TEST] Email suppressed - would send to ${Array.isArray(payload.to) ? payload.to.join(', ') : payload.to}: "${payload.subject}"`);
27
31
  return { data: { id: 'test-suppressed' }, error: null, headers: null };
28
32
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim",
3
- "version": "2.0.210",
3
+ "version": "2.0.211",
4
4
  "description": "FRAIM core CLI and MCP package.",
5
5
  "main": "index.js",
6
6
  "bin": {