fraim-framework 2.0.166 → 2.0.167

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 (42) hide show
  1. package/dist/src/ai-hub/catalog.js +20 -27
  2. package/dist/src/ai-hub/server.js +418 -2
  3. package/dist/src/config/ai-manager-hiring.js +121 -0
  4. package/dist/src/config/compat.js +16 -0
  5. package/dist/src/config/feature-flags.js +25 -0
  6. package/dist/src/config/persona-capability-bundles.js +273 -0
  7. package/dist/src/config/persona-hiring.js +270 -0
  8. package/dist/src/config/portfolio-slug-overrides.js +17 -0
  9. package/dist/src/config/pricing.js +37 -0
  10. package/dist/src/config/stripe.js +43 -0
  11. package/dist/src/core/config-loader.js +9 -5
  12. package/dist/src/core/fraim-config-schema.generated.js +0 -21
  13. package/dist/src/core/utils/local-registry-resolver.js +8 -1
  14. package/package.json +5 -1
  15. package/public/ai-hub/index.html +81 -0
  16. package/public/ai-hub/review.css +13 -0
  17. package/public/ai-hub/script.js +414 -4
  18. package/public/ai-hub/styles.css +56 -0
  19. package/public/portfolio/ashley.html +523 -0
  20. package/public/portfolio/auditya.html +83 -0
  21. package/public/portfolio/banke.html +83 -0
  22. package/public/portfolio/beza.html +659 -0
  23. package/public/portfolio/careena.html +632 -0
  24. package/public/portfolio/casey.html +568 -0
  25. package/public/portfolio/celia.html +490 -0
  26. package/public/portfolio/deidre.html +642 -0
  27. package/public/portfolio/gautam.html +597 -0
  28. package/public/portfolio/hari.html +469 -0
  29. package/public/portfolio/huxley.html +1354 -0
  30. package/public/portfolio/index.html +741 -0
  31. package/public/portfolio/maestro.html +518 -0
  32. package/public/portfolio/mandy.html +590 -0
  33. package/public/portfolio/mona.html +597 -0
  34. package/public/portfolio/pam.html +887 -0
  35. package/public/portfolio/procella.html +107 -0
  36. package/public/portfolio/qasm.html +569 -0
  37. package/public/portfolio/ricardo.html +489 -0
  38. package/public/portfolio/sade.html +560 -0
  39. package/public/portfolio/sam.html +654 -0
  40. package/public/portfolio/sechar.html +580 -0
  41. package/public/portfolio/sreya.html +599 -0
  42. package/public/portfolio/swen.html +601 -0
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PERSONA_AVATAR_CATALOG = exports.PERSONA_HIRE_CATALOG = void 0;
4
+ exports.buildPersonaAvatarUrl = buildPersonaAvatarUrl;
5
+ exports.isPersonaHireKey = isPersonaHireKey;
6
+ exports.getPersonaHireAmountCents = getPersonaHireAmountCents;
7
+ /**
8
+ * PERSONA_HIRE_CATALOG — single source of truth for all AI employee identity,
9
+ * pricing, and card-display fields.
10
+ *
11
+ * Fields added in issue #545:
12
+ * emoji — avatar icon rendered on persona cards
13
+ * gradient — CSS gradient string for card background accent
14
+ * blurb — 1-2 sentence marketing description shown on persona cards
15
+ *
16
+ * These were previously hard-coded in fraim-pro/index.html and
17
+ * fraim-pro/pricing.html. Moving them here makes this catalog the single
18
+ * authoritative record for everything a card needs to render, served via
19
+ * GET /api/personas/catalog.
20
+ */
21
+ exports.PERSONA_HIRE_CATALOG = {
22
+ maestro: {
23
+ displayName: 'MAESTRO',
24
+ role: 'Full-Brained AI Employee',
25
+ emoji: '★',
26
+ gradient: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #d946ef 100%)',
27
+ blurb: 'One AI employee who can take a job from any function and ship it back with evidence. You set the direction. You sign off on what ships. Maestro does the work.',
28
+ jobPriceCents: 24900, // $249 — full-time only; job mode maps to same price
29
+ fulltimePriceCents: 24900, // $249/mo
30
+ },
31
+ beza: {
32
+ displayName: 'BeZa',
33
+ role: 'AI Business Strategist',
34
+ emoji: '🧭',
35
+ gradient: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
36
+ blurb: 'Turns ideas into structured business plans, validates founder-market fit, and pressure-tests strategy.',
37
+ jobPriceCents: 990,
38
+ fulltimePriceCents: 4990,
39
+ },
40
+ pam: {
41
+ displayName: 'PaM',
42
+ role: 'AI Product Manager',
43
+ emoji: '📋',
44
+ gradient: 'linear-gradient(135deg, #8b5cf6 0%, #d946ef 100%)',
45
+ blurb: 'Owns specs, PRDs, technical design, issue prep, and the path from idea to shippable artifact.',
46
+ jobPriceCents: 790,
47
+ fulltimePriceCents: 4990,
48
+ },
49
+ swen: {
50
+ displayName: 'SWEn',
51
+ role: 'AI Software Engineer',
52
+ emoji: '💻',
53
+ gradient: 'linear-gradient(135deg, #2563eb 0%, #06b6d4 100%)',
54
+ blurb: 'Implements features, refactors code, and drives PR iteration to merge, reviewing design and code as a senior would.',
55
+ jobPriceCents: 1290,
56
+ fulltimePriceCents: 5990,
57
+ },
58
+ qasm: {
59
+ displayName: 'QAsm',
60
+ role: 'AI QA Engineer',
61
+ emoji: '🛡️',
62
+ gradient: 'linear-gradient(135deg, #10b981 0%, #14b8a6 100%)',
63
+ blurb: 'Runs tests, assesses code and test quality, polishes UI, and drives bug bashes with evidence.',
64
+ jobPriceCents: 590,
65
+ fulltimePriceCents: 3990,
66
+ },
67
+ huxley: {
68
+ displayName: 'hUXley',
69
+ role: 'AI UX / Brand Designer',
70
+ emoji: '🎨',
71
+ gradient: 'linear-gradient(135deg, #ec4899 0%, #f472b6 100%)',
72
+ blurb: 'Builds design systems, prototypes polished user-facing surfaces, and carries brand decisions into shipped product experiences.',
73
+ jobPriceCents: 1490,
74
+ fulltimePriceCents: 5490,
75
+ },
76
+ gautam: {
77
+ displayName: 'GauTaM',
78
+ role: 'AI GTM & Marketing Manager',
79
+ emoji: '📣',
80
+ gradient: 'linear-gradient(135deg, #f97316 0%, #f59e0b 100%)',
81
+ blurb: 'Defines marketing strategy, ships content, runs launches, and owns the brand voice in market.',
82
+ jobPriceCents: 890,
83
+ fulltimePriceCents: 4990,
84
+ },
85
+ cela: {
86
+ displayName: 'CELiA',
87
+ role: 'AI Legal Counsel',
88
+ emoji: '⚖️',
89
+ gradient: 'linear-gradient(135deg, #475569 0%, #6366f1 100%)',
90
+ blurb: 'Drafts and reviews contracts, NDAs, patents, trademarks, and the SaaS legal stack.',
91
+ jobPriceCents: 1990,
92
+ fulltimePriceCents: 6990,
93
+ },
94
+ sekhar: {
95
+ displayName: 'SEChar',
96
+ role: 'AI Security Engineer',
97
+ emoji: '🔒',
98
+ gradient: 'linear-gradient(135deg, #ef4444 0%, #f43f5e 100%)',
99
+ blurb: 'Sets up AI-native security baselines, runs the findings command center, reviews changes for risk, and drives remediation to closure.',
100
+ jobPriceCents: 1790,
101
+ fulltimePriceCents: 6490,
102
+ },
103
+ ashley: {
104
+ displayName: 'AshLey',
105
+ role: 'AI Executive Assistant',
106
+ emoji: '📅',
107
+ gradient: 'linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%)',
108
+ blurb: 'Owns executive coordination, weekly operating reviews, and portfolio reporting across the workforce.',
109
+ jobPriceCents: 490,
110
+ fulltimePriceCents: 2990,
111
+ },
112
+ mandy: {
113
+ displayName: 'MANdy',
114
+ role: 'AI Manager',
115
+ emoji: '🎯',
116
+ gradient: 'linear-gradient(135deg, #7c3aed 0%, #4338ca 100%)',
117
+ blurb: 'Plans the job sequence, runs sub-agents in parallel, coaches them through verification loops, and hands back a synthesized DRAFT for your approval.',
118
+ jobPriceCents: 1490,
119
+ fulltimePriceCents: 5990,
120
+ },
121
+ ricardo: {
122
+ displayName: 'RECardo',
123
+ role: 'AI Recruiter',
124
+ emoji: '🤝',
125
+ gradient: 'linear-gradient(135deg, #6366f1 0%, #ec4899 100%)',
126
+ blurb: 'Sources candidates, writes job descriptions, screens pipelines, and manages the hiring loop end-to-end.',
127
+ jobPriceCents: 790,
128
+ fulltimePriceCents: 4490,
129
+ },
130
+ hari: {
131
+ displayName: 'HaRi',
132
+ role: 'AI HR Manager',
133
+ emoji: '👥',
134
+ gradient: 'linear-gradient(135deg, #0d9488 0%, #059669 100%)',
135
+ blurb: 'Manages onboarding, performance reviews, benefits analysis, payroll coordination, and HR business-partner advisory.',
136
+ jobPriceCents: 790,
137
+ fulltimePriceCents: 4490,
138
+ },
139
+ careena: {
140
+ displayName: 'CAREEna',
141
+ role: 'AI Career Coach',
142
+ emoji: '🎓',
143
+ gradient: 'linear-gradient(135deg, #0ea5e9 0%, #6366f1 100%)',
144
+ blurb: 'Runs the candidate-side search loop: role sourcing, application execution, networking, interview prep, and close-stage offer strategy.',
145
+ jobPriceCents: 890,
146
+ fulltimePriceCents: 4990,
147
+ },
148
+ sade: {
149
+ displayName: 'SADE',
150
+ role: 'AI Salesforce Developer',
151
+ emoji: '☁️',
152
+ gradient: 'linear-gradient(135deg, #0284c7 0%, #0369a1 100%)',
153
+ blurb: 'Deploys Salesforce configuration from ServiceNow tickets, builds reports and dashboards, creates Flows from business requirements, audits org health, and manages users and data at scale.',
154
+ jobPriceCents: 1490,
155
+ fulltimePriceCents: 6490,
156
+ },
157
+ sam: {
158
+ displayName: 'SAM',
159
+ role: 'AI Sales Account Manager',
160
+ emoji: '📈',
161
+ gradient: 'linear-gradient(135deg, #059669 0%, #0d9488 100%)',
162
+ blurb: 'Surfaces stalled deals, computes pipeline health, and drafts account-specific re-engagement proposals for your top at-risk opportunities.',
163
+ jobPriceCents: 1290,
164
+ fulltimePriceCents: 5990,
165
+ },
166
+ casey: {
167
+ displayName: 'CaSey',
168
+ role: 'AI Customer Success + Support',
169
+ emoji: '💬',
170
+ gradient: 'linear-gradient(135deg, #db2777 0%, #9333ea 100%)',
171
+ blurb: 'Scores account churn risk and expansion potential, generates prioritized action plans for CSMs, triages support cases, and routes L1 cases through automated resolution.',
172
+ jobPriceCents: 990,
173
+ fulltimePriceCents: 4990,
174
+ },
175
+ deidre: {
176
+ displayName: 'DEIdre',
177
+ role: 'AI Inclusion Leader',
178
+ emoji: '🌍',
179
+ gradient: 'linear-gradient(135deg, #9333ea 0%, #d946ef 100%)',
180
+ blurb: 'Audits equity gaps, designs bias-aware AI governance, builds ERG toolkits, and creates inclusion-fluency programs.',
181
+ jobPriceCents: 890,
182
+ fulltimePriceCents: 4490,
183
+ },
184
+ mona: {
185
+ displayName: 'MONa',
186
+ role: 'AI Finance Manager',
187
+ emoji: '💰',
188
+ gradient: 'linear-gradient(135deg, #10b981 0%, #f59e0b 100%)',
189
+ blurb: 'Models revenue, tracks unit economics, builds financial forecasts, and owns the metrics that drive growth decisions.',
190
+ jobPriceCents: 990,
191
+ fulltimePriceCents: 4990,
192
+ },
193
+ sreya: {
194
+ displayName: 'SREya',
195
+ role: 'AI Site Reliability Engineer',
196
+ emoji: '☁️',
197
+ gradient: 'linear-gradient(135deg, #2563eb 0%, #10b981 100%)',
198
+ blurb: 'Manages deployments, monitors uptime, optimizes cloud cost, and keeps infrastructure resilient and observable.',
199
+ jobPriceCents: 1290,
200
+ fulltimePriceCents: 5990,
201
+ },
202
+ procella: {
203
+ displayName: 'PROCella',
204
+ role: 'AI Procurement Manager',
205
+ emoji: '📦',
206
+ gradient: 'linear-gradient(135deg, #0f766e 0%, #7c3aed 100%)',
207
+ blurb: 'Frames procurement strategy, sources suppliers, runs RFx packages, evaluates responses, and keeps purchases acceptance-gated.',
208
+ jobPriceCents: 990,
209
+ fulltimePriceCents: 4990,
210
+ },
211
+ banke: {
212
+ displayName: 'BANKe',
213
+ role: 'AI Banking KYC Employee',
214
+ emoji: '\u{1F3E6}',
215
+ gradient: 'linear-gradient(135deg, #0f766e 0%, #2563eb 100%)',
216
+ blurb: 'Runs banking KYC cases with consent-aware evidence capture, decision receipts, and stable audit handoff artifacts.',
217
+ jobPriceCents: 1490,
218
+ fulltimePriceCents: 6490,
219
+ },
220
+ auditya: {
221
+ displayName: 'AUDITya',
222
+ role: 'AI Banking Auditor',
223
+ emoji: '\u{1F50E}',
224
+ gradient: 'linear-gradient(135deg, #7c3aed 0%, #0f172a 100%)',
225
+ blurb: 'Audits AI banking work by tracing decisions back to evidence, receipts, exceptions, and reusable audit reports.',
226
+ jobPriceCents: 1490,
227
+ fulltimePriceCents: 6490,
228
+ },
229
+ };
230
+ exports.PERSONA_AVATAR_CATALOG = {
231
+ maestro: { seed: 'MAESTRO-founder-mode', bg: 'fde68a', style: 'notionists' },
232
+ beza: { seed: 'BEZA-strategist', bg: 'c7d2fe', style: 'notionists' },
233
+ pam: { seed: 'PAM-product', bg: 'ddd6fe', style: 'notionists' },
234
+ swen: { seed: 'SWEN-engineer', bg: 'bfdbfe', style: 'notionists' },
235
+ qasm: { seed: 'QASM-quality', bg: 'a7f3d0', style: 'notionists' },
236
+ huxley: { seed: 'HUXLEY-design', bg: 'fbcfe8', style: 'notionists' },
237
+ gautam: { seed: 'Gautam-marketing', bg: 'fed7aa', style: 'notionists' },
238
+ cela: { seed: 'CELA-legal', bg: 'cbd5e1', style: 'notionists' },
239
+ sekhar: { seed: 'SEKHAR-security', bg: 'fecaca', style: 'notionists' },
240
+ ashley: { seed: 'Ashley-assistant', bg: 'fde68a', style: 'notionists' },
241
+ mandy: { seed: 'MANDY-manager', bg: 'ede9fe', style: 'notionists' },
242
+ ricardo: { seed: 'RICARDO-recruiter', bg: 'ede9fe', style: 'notionists' },
243
+ hari: { seed: 'HARI-hr', bg: 'ccfbf1', style: 'notionists' },
244
+ careena: { seed: 'CAREENA-career-coach', bg: 'e0f2fe', style: 'notionists' },
245
+ sade: { seed: 'SADE-salesforce', bg: 'bae6fd', style: 'notionists' },
246
+ sam: { seed: 'SAM-sales-account', bg: 'd1fae5', style: 'notionists' },
247
+ casey: { seed: 'CASEY-cx', bg: 'a5f3fc', style: 'notionists' },
248
+ deidre: { seed: 'DEIDRE-inclusion-leader', bg: 'e9d5ff', style: 'notionists' },
249
+ mona: { seed: 'MONA-finance', bg: 'd1fae5', style: 'notionists' },
250
+ sreya: { seed: 'SREYA-sre-cloud', bg: 'bbf7d0', style: 'notionists' },
251
+ procella: { seed: 'PROCELLA-procurement', bg: 'ccfbf1', style: 'notionists' },
252
+ banke: { seed: 'BANKe-banking-kyc', bg: 'ccfbf1', style: 'notionists' },
253
+ auditya: { seed: 'AUDITya-banking-audit', bg: 'e9d5ff', style: 'notionists' },
254
+ };
255
+ function buildPersonaAvatarUrl(personaKey) {
256
+ const avatar = exports.PERSONA_AVATAR_CATALOG[personaKey];
257
+ const params = new URLSearchParams({
258
+ seed: avatar.seed,
259
+ backgroundColor: avatar.bg,
260
+ radius: '50',
261
+ });
262
+ return `https://api.dicebear.com/9.x/${avatar.style}/svg?${params.toString()}`;
263
+ }
264
+ function isPersonaHireKey(value) {
265
+ return value in exports.PERSONA_HIRE_CATALOG;
266
+ }
267
+ function getPersonaHireAmountCents(personaKey, mode) {
268
+ const persona = exports.PERSONA_HIRE_CATALOG[personaKey];
269
+ return mode === 'fulltime' ? persona.fulltimePriceCents : persona.jobPriceCents;
270
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PORTFOLIO_SLUG_OVERRIDES = void 0;
4
+ /**
5
+ * Portfolio filename slug overrides.
6
+ *
7
+ * When a persona's portfolio HTML file name differs from the persona key,
8
+ * add an entry here. Both `scripts/validate-employee-catalog.ts` and
9
+ * `src/routes/persona-catalog-routes.ts` import from this single module so
10
+ * the override map is defined exactly once.
11
+ *
12
+ * Issue #545.
13
+ */
14
+ exports.PORTFOLIO_SLUG_OVERRIDES = {
15
+ cela: 'celia',
16
+ sekhar: 'sechar',
17
+ };
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ // Shared pricing configuration
3
+ // Used by both backend (payment-calculator.ts) and frontend (payment.js via API)
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.CONSUMER_EMAIL_DOMAINS = exports.FOUNDER_DISCOUNT_RATE = exports.MANAGED_PRICING = exports.FIXED_FEES = exports.PRICING = void 0;
6
+ exports.PRICING = {
7
+ 'self-served': {
8
+ monthly: 20000, // $200/user/month
9
+ annual: 199200, // $200/user/month * 12 months * 0.83 (17% annual discount)
10
+ },
11
+ 'managed': {
12
+ monthly: 20000, // $200/user/month (license cost)
13
+ annual: 199200, // $200/user/month * 12 months * 0.83 (17% annual discount)
14
+ },
15
+ };
16
+ exports.FIXED_FEES = {
17
+ 'self-served': 0,
18
+ 'managed': 0,
19
+ };
20
+ // Managed plan: $200/user/month (license) + $10K per group of 10 (training/support)
21
+ exports.MANAGED_PRICING = {
22
+ trainingFeePerGroup: 1000000, // $10,000 training/support per group of 10 licenses
23
+ licensesPerGroup: 10,
24
+ monthly: 1000000, // $10,000 training fee per group/month
25
+ annual: 9960000, // $10,000 training fee per group/month * 12 months * 0.83 (17% annual discount)
26
+ };
27
+ exports.FOUNDER_DISCOUNT_RATE = 0.90; // Discount rate for startups and academia (90% off)
28
+ exports.CONSUMER_EMAIL_DOMAINS = [
29
+ 'gmail.com',
30
+ 'yahoo.com',
31
+ 'hotmail.com',
32
+ 'outlook.com',
33
+ 'aol.com',
34
+ 'icloud.com',
35
+ 'protonmail.com',
36
+ 'mail.com',
37
+ ];
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.STRIPE_CONFIG = exports.stripe = void 0;
7
+ const stripe_1 = __importDefault(require("stripe"));
8
+ // Lazy initialization to allow environment variables to be loaded first
9
+ let stripeInstance = null;
10
+ function getStripe() {
11
+ if (!stripeInstance) {
12
+ if (!process.env.STRIPE_SECRET_KEY) {
13
+ throw new Error('STRIPE_SECRET_KEY environment variable is required');
14
+ }
15
+ stripeInstance = new stripe_1.default(process.env.STRIPE_SECRET_KEY, {
16
+ apiVersion: '2026-01-28.clover',
17
+ typescript: true,
18
+ });
19
+ }
20
+ return stripeInstance;
21
+ }
22
+ exports.stripe = new Proxy({}, {
23
+ get(target, prop) {
24
+ return getStripe()[prop];
25
+ }
26
+ });
27
+ exports.STRIPE_CONFIG = {
28
+ get publishableKey() {
29
+ if (!process.env.STRIPE_PUBLISHABLE_KEY) {
30
+ throw new Error('STRIPE_PUBLISHABLE_KEY environment variable is required');
31
+ }
32
+ return process.env.STRIPE_PUBLISHABLE_KEY;
33
+ },
34
+ get webhookSecret() {
35
+ if (!process.env.STRIPE_WEBHOOK_SECRET) {
36
+ throw new Error('STRIPE_WEBHOOK_SECRET environment variable is required');
37
+ }
38
+ return process.env.STRIPE_WEBHOOK_SECRET;
39
+ },
40
+ currency: 'usd',
41
+ successUrl: process.env.STRIPE_SUCCESS_URL || 'https://fraimworks.ai/payment-success.html',
42
+ cancelUrl: process.env.STRIPE_CANCEL_URL || 'https://fraimworks.ai/pricing.html',
43
+ };
@@ -33,7 +33,8 @@ function normalizeIntegrations(config) {
33
33
  itsm: current.itsm && typeof current.itsm === 'object'
34
34
  ? {
35
35
  provider: current.itsm.provider,
36
- instanceUrl: current.itsm.instanceUrl
36
+ instanceUrl: current.itsm.instanceUrl,
37
+ accessScript: current.itsm.accessScript
37
38
  }
38
39
  : undefined,
39
40
  identity: current.identity && typeof current.identity === 'object'
@@ -49,13 +50,16 @@ function normalizeAutomation(config) {
49
50
  support: {
50
51
  startMode: support.startMode,
51
52
  defaultDecisionMode: support.defaultDecisionMode,
52
- contextResolver: support.contextResolver && typeof support.contextResolver === 'object'
53
+ playbooks: support.playbooks && typeof support.playbooks === 'object'
53
54
  ? {
54
- scriptPath: support.contextResolver.scriptPath,
55
- arguments: Array.isArray(support.contextResolver.arguments) ? support.contextResolver.arguments : undefined,
56
- timeoutMs: support.contextResolver.timeoutMs
55
+ directory: support.playbooks.directory,
56
+ decisionCommand: support.playbooks.decisionCommand,
57
+ decisionTimeoutMs: support.playbooks.decisionTimeoutMs
57
58
  }
58
59
  : undefined,
60
+ actionAdapters: support.actionAdapters && typeof support.actionAdapters === 'object'
61
+ ? support.actionAdapters
62
+ : undefined,
59
63
  queue: support.queue && typeof support.queue === 'object'
60
64
  ? {
61
65
  provider: support.queue.provider,
@@ -335,23 +335,6 @@ exports.FRAIM_CONFIG_SCHEMA = {
335
335
  "autonomous"
336
336
  ]
337
337
  },
338
- "contextResolver": {
339
- "kind": "object",
340
- "properties": {
341
- "scriptPath": {
342
- "kind": "string"
343
- },
344
- "arguments": {
345
- "kind": "array",
346
- "element": {
347
- "kind": "string"
348
- }
349
- },
350
- "timeoutMs": {
351
- "kind": "number"
352
- }
353
- }
354
- },
355
338
  "playbooks": {
356
339
  "kind": "object",
357
340
  "properties": {
@@ -604,10 +587,6 @@ exports.SUPPORTED_FRAIM_CONFIG_PATHS = [
604
587
  "automation.support",
605
588
  "automation.support.startMode",
606
589
  "automation.support.defaultDecisionMode",
607
- "automation.support.contextResolver",
608
- "automation.support.contextResolver.scriptPath",
609
- "automation.support.contextResolver.arguments",
610
- "automation.support.contextResolver.timeoutMs",
611
590
  "automation.support.playbooks",
612
591
  "automation.support.playbooks.directory",
613
592
  "automation.support.playbooks.decisionCommand",
@@ -371,9 +371,11 @@ class LocalRegistryResolver {
371
371
  if (!this.hasLocalOverride(path)) {
372
372
  const syncedLocalContent = this.readSyncedLocalFile(path);
373
373
  if (syncedLocalContent !== null) {
374
+ // Synced baseline (fraim/ai-employee), not personalized.
374
375
  return {
375
376
  content: syncedLocalContent,
376
377
  source: 'local',
378
+ personalized: false,
377
379
  inherited: false
378
380
  };
379
381
  }
@@ -384,6 +386,7 @@ class LocalRegistryResolver {
384
386
  return {
385
387
  content,
386
388
  source: 'remote',
389
+ personalized: false,
387
390
  inherited: false
388
391
  };
389
392
  }
@@ -408,6 +411,7 @@ class LocalRegistryResolver {
408
411
  return {
409
412
  content,
410
413
  source: 'remote',
414
+ personalized: false,
411
415
  inherited: false
412
416
  };
413
417
  }
@@ -422,13 +426,16 @@ class LocalRegistryResolver {
422
426
  return {
423
427
  content,
424
428
  source: 'remote',
429
+ personalized: false,
425
430
  inherited: false
426
431
  };
427
432
  }
428
- // Build result
433
+ // Build result — a local override hit means this came from the
434
+ // personalized-employee layer (the manager taught/customized it).
429
435
  const result = {
430
436
  content: resolved.content,
431
437
  source: 'local',
438
+ personalized: true,
432
439
  inherited: resolved.imports.length > 0,
433
440
  imports: resolved.imports.length > 0 ? resolved.imports : undefined
434
441
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim-framework",
3
- "version": "2.0.166",
3
+ "version": "2.0.167",
4
4
  "description": "FRAIM: AI Workforce Infrastructure — the organizational capability that turns AI agents into an accountable workforce, their operators into capable AI managers, and executives into leaders with clear optics on AI proficiency.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -100,12 +100,14 @@
100
100
  "@types/cors": "^2.8.19",
101
101
  "@types/express": "^5.0.6",
102
102
  "@types/node": "^20.0.0",
103
+ "@types/node-cron": "^3.0.11",
103
104
  "@types/node-fetch": "^2.6.13",
104
105
  "@types/prompts": "^2.4.9",
105
106
  "@types/semver": "^7.7.1",
106
107
  "fast-glob": "^3.3.3",
107
108
  "markdown-it": "^14.1.1",
108
109
  "markdown-it-highlightjs": "^4.3.0",
110
+ "node-cron": "^4.2.1",
109
111
  "playwright": "^1.58.2",
110
112
  "pptxgenjs": "^4.0.1",
111
113
  "puppeteer": "^24.36.1",
@@ -120,10 +122,12 @@
120
122
  "dist/src/ai-hub/",
121
123
  "dist/src/first-run/",
122
124
  "dist/src/core/",
125
+ "dist/src/config/",
123
126
  "bin/fraim.js",
124
127
  "bin/fraim-mcp.js",
125
128
  "public/ai-hub/",
126
129
  "public/first-run/",
130
+ "public/portfolio/",
127
131
  "index.js",
128
132
  "README.md",
129
133
  "CHANGELOG.md",
@@ -194,6 +194,18 @@
194
194
  <span class="ca-note">— from <strong>sleep-on-learnings</strong> · promote to a scope</span></summary>
195
195
  <div class="ctx-acc-body"><div id="project-learnings"></div></div>
196
196
  </details>
197
+ <!-- Issue #578: Scheduled + Reactive Employees — deployment roster -->
198
+ <details class="ctx-acc" id="proj-deployments-acc">
199
+ <summary><span class="ca-chev">▸</span> <span>⏱ Deployments</span>
200
+ <span class="ca-note">— scheduled and webhook triggers for this project</span></summary>
201
+ <div class="ctx-acc-body">
202
+ <div id="proj-deployments-list"></div>
203
+ <div class="dep-actions">
204
+ <button class="dep-add-btn" id="dep-add-schedule-btn" type="button">+ Schedule</button>
205
+ <button class="dep-add-btn" id="dep-add-webhook-btn" type="button">+ Webhook</button>
206
+ </div>
207
+ </div>
208
+ </details>
197
209
  </div>
198
210
 
199
211
 
@@ -668,6 +680,75 @@
668
680
  </div>
669
681
  </div>
670
682
 
683
+ <!-- Issue #578: Add Schedule deployment modal -->
684
+ <div id="dep-schedule-modal" class="modal-overlay" hidden>
685
+ <div class="modal-card" style="max-width:480px;">
686
+ <div class="modal-hdr">
687
+ <button class="modal-close" id="dep-schedule-close" type="button" aria-label="Close">×</button>
688
+ <h2>New Scheduled Deployment</h2>
689
+ </div>
690
+ <div class="modal-body">
691
+ <div class="hm-field">
692
+ <label for="dep-sch-label">Label</label>
693
+ <input type="text" id="dep-sch-label" placeholder="e.g. Nightly standup" />
694
+ </div>
695
+ <div class="hm-field">
696
+ <label for="dep-sch-job">Job</label>
697
+ <select id="dep-sch-job"></select>
698
+ </div>
699
+ <div class="hm-field">
700
+ <label for="dep-sch-cron">Cron expression</label>
701
+ <input type="text" id="dep-sch-cron" placeholder="e.g. 0 9 * * 1-5 (9am Mon-Fri)" />
702
+ </div>
703
+ <div class="hm-field">
704
+ <label for="dep-sch-instructions">Initial instructions (optional)</label>
705
+ <textarea id="dep-sch-instructions" rows="2" placeholder="Optional coaching message to send at run start"></textarea>
706
+ </div>
707
+ <div class="dep-modal-actions">
708
+ <button type="button" id="dep-sch-save-btn" class="send-button">Create schedule</button>
709
+ <button type="button" id="dep-sch-cancel-btn" class="cancel-button">Cancel</button>
710
+ </div>
711
+ <p id="dep-sch-error" class="dep-error" hidden></p>
712
+ </div>
713
+ </div>
714
+ </div>
715
+
716
+ <!-- Issue #578: Add Webhook deployment modal -->
717
+ <div id="dep-webhook-modal" class="modal-overlay" hidden>
718
+ <div class="modal-card" style="max-width:480px;">
719
+ <div class="modal-hdr">
720
+ <button class="modal-close" id="dep-webhook-close" type="button" aria-label="Close">×</button>
721
+ <h2>New Webhook Deployment</h2>
722
+ </div>
723
+ <div class="modal-body">
724
+ <div class="hm-field">
725
+ <label for="dep-wh-label">Label</label>
726
+ <input type="text" id="dep-wh-label" placeholder="e.g. ServiceNow escalation" />
727
+ </div>
728
+ <div class="hm-field">
729
+ <label for="dep-wh-job">Job</label>
730
+ <select id="dep-wh-job"></select>
731
+ </div>
732
+ <div class="hm-field">
733
+ <label for="dep-wh-instructions">Initial instructions (optional)</label>
734
+ <textarea id="dep-wh-instructions" rows="2" placeholder="Optional coaching message to prepend at run start"></textarea>
735
+ </div>
736
+ <div class="dep-modal-actions">
737
+ <button type="button" id="dep-wh-save-btn" class="send-button">Create webhook</button>
738
+ <button type="button" id="dep-wh-cancel-btn" class="cancel-button">Cancel</button>
739
+ </div>
740
+ <p id="dep-wh-error" class="dep-error" hidden></p>
741
+ <div id="dep-wh-inbound-row" hidden>
742
+ <label>Inbound URL (copy to your system)</label>
743
+ <div class="dep-inbound-url-row">
744
+ <code id="dep-wh-inbound-url" class="dep-inbound-url"></code>
745
+ <button type="button" id="dep-wh-copy-btn" class="hm-copy-btn">Copy</button>
746
+ </div>
747
+ </div>
748
+ </div>
749
+ </div>
750
+ </div>
751
+
671
752
  <!-- Issue #539: Command palette overlay — replaces 2-step modal as job-launch surface -->
672
753
  <div id="cp-modal" class="cp-backdrop" role="dialog" aria-modal="true" aria-label="Start a job" hidden>
673
754
  <div class="cp-palette">
@@ -70,6 +70,19 @@
70
70
  }
71
71
  .rc-art-btn.ghost:hover { background: var(--soft); }
72
72
 
73
+ /* ── Issue #566: success-confirmed growth offer + teach action ──────────────
74
+ Reuses the .review-card surface with the personalization "teach" accent
75
+ (defined in styles.css :root) so the offer reads as the employee proposing
76
+ to grow, distinct from a plain review card. */
77
+ .review-card--offer .rc-heading { color: var(--teach); }
78
+ .review-card--growth .rc-heading { color: var(--teach); }
79
+ .rc-art-btn--teach {
80
+ background: var(--teach-soft);
81
+ color: var(--teach);
82
+ border: 1px solid var(--teach-line);
83
+ }
84
+ .rc-art-btn--teach:hover { background: #ece3ff; }
85
+
73
86
  /* ── R7.7: unified action bar — review actions sit beside the always-on
74
87
  coaching chips inside the coach panel. The chips/input never disappear. ── */
75
88
  .review-actions {