fraim 2.0.359-next.0 → 2.0.359-next.1
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/src/config/persona-capability-bundles.js +3 -2
- package/dist/src/config/persona-hiring.js +2 -2
- package/dist/src/core/utils/inheritance-parser.js +14 -4
- package/dist/src/core/utils/local-registry-resolver.js +43 -47
- package/dist/src/services/registry-service.js +24 -3
- package/package.json +1 -1
|
@@ -90,7 +90,7 @@ exports.PERSONA_CAPABILITY_BUNDLES = {
|
|
|
90
90
|
personaKey: 'gautam',
|
|
91
91
|
bundleId: 'persona-gautam-core',
|
|
92
92
|
catalogMetadata: buildCatalogMetadata('gautam', ['analyze-revenue-system', 'build-gtm-motion', 'domain-registration-research']),
|
|
93
|
-
protectedJobs: ['marketing-strategy-definition', 'product-launch-management', 'evangelist-content-development', 'funnel-analysis', 'growth-loop-design', 'analyze-revenue-system', 'design-gtm-system', 'build-gtm-motion', 'build-gtm-stack', 'plan-gtm-team', 'marketing-content-creation', 'ppc-campaign-management', 'paid-social-strategy', 'ad-performance-analysis', 'tracking-and-attribution-setup', 'developer-advocacy', 'seo-strategy', 'marketing-analytics-review', 'linkedin-company-page-setup', 'x-account-setup', 'domain-registration-research', 'social-engagement-campaign', 'thought-leadership-engagement', 'promo-video-creation', 'linkedin-carousel-from-deck', 'notebooklm-content-generation'],
|
|
93
|
+
protectedJobs: ['marketing-strategy-definition', 'product-launch-management', 'evangelist-content-development', 'funnel-analysis', 'growth-loop-design', 'analyze-revenue-system', 'design-gtm-system', 'build-gtm-motion', 'build-gtm-stack', 'plan-gtm-team', 'marketing-content-creation', 'ppc-campaign-management', 'paid-social-strategy', 'ad-performance-analysis', 'tracking-and-attribution-setup', 'developer-advocacy', 'seo-strategy', 'aeo-strategy', 'marketing-analytics-review', 'linkedin-company-page-setup', 'x-account-setup', 'domain-registration-research', 'social-engagement-campaign', 'thought-leadership-engagement', 'promo-video-creation', 'linkedin-carousel-from-deck', 'notebooklm-content-generation'],
|
|
94
94
|
protectedAliases: ['gtm', 'marketing'],
|
|
95
95
|
defaultHireMode: 'job',
|
|
96
96
|
lockCopy: 'Hire GauTaM to unlock go-to-market and paid media work for this request.'
|
|
@@ -278,6 +278,7 @@ exports.PERSONA_CAPABILITY_BUNDLES = {
|
|
|
278
278
|
catalogMetadata: buildCatalogMetadata('aida', ['create-ai-agent', 'author-ai-evals', 'enable-web-mcp']),
|
|
279
279
|
protectedJobs: [
|
|
280
280
|
'create-ai-agent',
|
|
281
|
+
'aeo-optimization',
|
|
281
282
|
'author-ai-evals',
|
|
282
283
|
'enable-web-mcp',
|
|
283
284
|
'evaluate-ai-agent',
|
|
@@ -287,7 +288,7 @@ exports.PERSONA_CAPABILITY_BUNDLES = {
|
|
|
287
288
|
],
|
|
288
289
|
protectedAliases: ['ai-engineering', 'agent-engineering', 'ai-agents'],
|
|
289
290
|
defaultHireMode: 'job',
|
|
290
|
-
lockCopy: 'Hire AIDa to unlock AI agent design, MCP enablement,
|
|
291
|
+
lockCopy: 'Hire AIDa to unlock AI agent design, MCP enablement, evals, and answer-engine optimization for this request.'
|
|
291
292
|
}
|
|
292
293
|
};
|
|
293
294
|
const PROTECTED_JOB_TO_PERSONA = new Map();
|
|
@@ -24,7 +24,7 @@ exports.PERSONA_HIRE_CATALOG = {
|
|
|
24
24
|
role: 'AI Developer',
|
|
25
25
|
emoji: '\u{1F9E0}',
|
|
26
26
|
gradient: 'linear-gradient(135deg, #4f46e5 0%, #06b6d4 50%, #10b981 100%)',
|
|
27
|
-
blurb: '
|
|
27
|
+
blurb: 'Builds and evaluates production AI agents, connects tools and data, and implements tested answer-engine optimization for websites.',
|
|
28
28
|
jobPriceCents: 1290, // placeholder — product owner approval required before launch
|
|
29
29
|
fulltimePriceCents: 5990, // placeholder — product owner approval required before launch
|
|
30
30
|
},
|
|
@@ -96,7 +96,7 @@ exports.PERSONA_HIRE_CATALOG = {
|
|
|
96
96
|
role: 'GTM & Marketing Manager',
|
|
97
97
|
emoji: '📣',
|
|
98
98
|
gradient: 'linear-gradient(135deg, #f97316 0%, #f59e0b 100%)',
|
|
99
|
-
blurb: 'Defines marketing strategy, ships content, runs launches, and owns the brand voice in market.',
|
|
99
|
+
blurb: 'Defines marketing and answer-engine visibility strategy, ships content, runs launches, and owns the brand voice in market.',
|
|
100
100
|
jobPriceCents: 890,
|
|
101
101
|
fulltimePriceCents: 4990,
|
|
102
102
|
},
|
|
@@ -115,7 +115,7 @@ class InheritanceParser {
|
|
|
115
115
|
// Fetch parent content
|
|
116
116
|
let parentContent;
|
|
117
117
|
try {
|
|
118
|
-
parentContent = await options.fetchParent(sanitizedExtends);
|
|
118
|
+
parentContent = await options.fetchParent(sanitizedExtends, currentPath);
|
|
119
119
|
}
|
|
120
120
|
catch (error) {
|
|
121
121
|
throw new InheritanceError(`Failed to fetch extended parent content: ${sanitizedExtends}. ${error.message}`, sanitizedExtends);
|
|
@@ -151,7 +151,7 @@ class InheritanceParser {
|
|
|
151
151
|
// Fetch parent content
|
|
152
152
|
let parentContent;
|
|
153
153
|
try {
|
|
154
|
-
parentContent = await options.fetchParent(sanitized);
|
|
154
|
+
parentContent = await options.fetchParent(sanitized, currentPath);
|
|
155
155
|
}
|
|
156
156
|
catch (error) {
|
|
157
157
|
throw new InheritanceError(`Failed to fetch parent content: ${sanitized}. ${error.message}`, sanitized);
|
|
@@ -249,11 +249,21 @@ class InheritanceParser {
|
|
|
249
249
|
*/
|
|
250
250
|
parse(content) {
|
|
251
251
|
const imports = this.extractImports(content);
|
|
252
|
-
const
|
|
252
|
+
const metadataMatch = content.match(/^---\r?\n([\s\S]+?)\r?\n---/);
|
|
253
|
+
if (metadataMatch) {
|
|
254
|
+
try {
|
|
255
|
+
const parent = JSON.parse(metadataMatch[1]).extends;
|
|
256
|
+
if (typeof parent === 'string' && parent && !imports.includes(parent))
|
|
257
|
+
imports.unshift(parent);
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
// Invalid/non-JSON frontmatter has no executable extends directive.
|
|
261
|
+
}
|
|
262
|
+
}
|
|
253
263
|
return {
|
|
254
264
|
content,
|
|
255
265
|
imports,
|
|
256
|
-
hasImports: imports.length > 0
|
|
266
|
+
hasImports: imports.length > 0
|
|
257
267
|
};
|
|
258
268
|
}
|
|
259
269
|
normalizeImportRef(path) {
|
|
@@ -102,7 +102,7 @@ class LocalRegistryResolver {
|
|
|
102
102
|
* Returns '{type}/{category}/{name}.md' or '{type}/{name}.md'.
|
|
103
103
|
*/
|
|
104
104
|
async findRegistryPath(type, name) {
|
|
105
|
-
const baseName = `${name}.md`;
|
|
105
|
+
const baseName = `${name.split(/[\\/]/).pop()}.md`;
|
|
106
106
|
const searchDirs = [type];
|
|
107
107
|
for (const dir of searchDirs) {
|
|
108
108
|
// Check literal path first
|
|
@@ -126,7 +126,9 @@ class LocalRegistryResolver {
|
|
|
126
126
|
...(type !== 'skills' ? [this.getSyncedCatalogRoot('ai-manager', dir)] : []),
|
|
127
127
|
];
|
|
128
128
|
for (const root of searchRoots) {
|
|
129
|
-
const
|
|
129
|
+
const namedPath = (0, path_1.join)(root, `${name}.md`);
|
|
130
|
+
const contained = (0, path_1.resolve)(namedPath).startsWith((0, path_1.resolve)(root) + path_1.sep);
|
|
131
|
+
const found = contained && (0, fs_1.existsSync)(namedPath) ? namedPath : this.searchFileRecursively(root, baseName);
|
|
130
132
|
if (!found)
|
|
131
133
|
continue;
|
|
132
134
|
const syncedRole = ['ai-employee', 'ai-manager'].find(role => root === this.getSyncedCatalogRoot(role, dir));
|
|
@@ -415,7 +417,7 @@ class LocalRegistryResolver {
|
|
|
415
417
|
/**
|
|
416
418
|
* Resolve inheritance in local override content
|
|
417
419
|
*/
|
|
418
|
-
async resolveInheritance(content, currentPath) {
|
|
420
|
+
async resolveInheritance(content, currentPath, personalized = true) {
|
|
419
421
|
// Check if content has imports
|
|
420
422
|
const parseResult = this.parser.parse(content);
|
|
421
423
|
if (!parseResult.hasImports) {
|
|
@@ -423,13 +425,14 @@ class LocalRegistryResolver {
|
|
|
423
425
|
}
|
|
424
426
|
// Resolve inheritance
|
|
425
427
|
try {
|
|
428
|
+
const personalizedPaths = new Set(personalized ? [this.normalizeRegistryPath(currentPath)] : []);
|
|
426
429
|
const resolved = await this.parser.resolve(content, currentPath, {
|
|
427
|
-
fetchParent: async (path) => {
|
|
430
|
+
fetchParent: async (path, requestingPath = currentPath) => {
|
|
428
431
|
const normalized = this.normalizeRegistryPath(path);
|
|
429
|
-
const normalizedCurrent = this.normalizeRegistryPath(
|
|
432
|
+
const normalizedCurrent = this.normalizeRegistryPath(requestingPath);
|
|
430
433
|
// If the import points back to the current file, skip local resolution and fetch
|
|
431
434
|
// from the next layer (remote/global) instead.
|
|
432
|
-
if (this.areEquivalentRegistryPaths(normalized, normalizedCurrent)) {
|
|
435
|
+
if (personalizedPaths.has(normalizedCurrent) && this.areEquivalentRegistryPaths(normalized, normalizedCurrent)) {
|
|
433
436
|
return this.fetchRemoteWithFallback(normalizedCurrent, normalized);
|
|
434
437
|
}
|
|
435
438
|
// Otherwise, resolve through the normal local-first path.
|
|
@@ -449,17 +452,21 @@ class LocalRegistryResolver {
|
|
|
449
452
|
}
|
|
450
453
|
}
|
|
451
454
|
}
|
|
452
|
-
|
|
455
|
+
// Fetch the selected layer's raw parent. The parser owns recursion,
|
|
456
|
+
// including its visited set and depth limit; restarting resolveFile
|
|
457
|
+
// here loses both guards for multi-file inheritance cycles.
|
|
458
|
+
const res = await this.resolveFileInternal(targetPath, {
|
|
453
459
|
includeMetadata: false,
|
|
454
460
|
stripMcpHeader: true
|
|
455
|
-
});
|
|
461
|
+
}, false);
|
|
462
|
+
if (res.personalized)
|
|
463
|
+
personalizedPaths.add(normalized);
|
|
456
464
|
return res.content;
|
|
457
465
|
}
|
|
458
466
|
catch (error) {
|
|
459
467
|
throw error;
|
|
460
468
|
}
|
|
461
|
-
}
|
|
462
|
-
maxDepth: 5
|
|
469
|
+
}
|
|
463
470
|
});
|
|
464
471
|
return {
|
|
465
472
|
content: resolved,
|
|
@@ -497,8 +504,25 @@ class LocalRegistryResolver {
|
|
|
497
504
|
* @returns Resolved file with metadata
|
|
498
505
|
*/
|
|
499
506
|
async resolveFile(path, options = {}) {
|
|
507
|
+
return this.resolveFileInternal(path, options, true);
|
|
508
|
+
}
|
|
509
|
+
async resolveFileInternal(path, options, applyInheritance) {
|
|
500
510
|
const includeMetadata = options.includeMetadata ?? true;
|
|
501
511
|
const stripMcpHeader = options.stripMcpHeader ?? false;
|
|
512
|
+
const resolveContent = (content, personalized = true) => applyInheritance
|
|
513
|
+
? this.resolveInheritance(content, path, personalized)
|
|
514
|
+
: Promise.resolve({ content, imports: [] });
|
|
515
|
+
const baselineResult = async (rawContent, source) => {
|
|
516
|
+
const content = stripMcpHeader ? this.stripMcpHeader(rawContent) : rawContent;
|
|
517
|
+
const resolved = await resolveContent(content, false);
|
|
518
|
+
return {
|
|
519
|
+
content: resolved.content,
|
|
520
|
+
source,
|
|
521
|
+
personalized: false,
|
|
522
|
+
inherited: resolved.imports.length > 0,
|
|
523
|
+
imports: resolved.imports.length > 0 ? resolved.imports : undefined
|
|
524
|
+
};
|
|
525
|
+
};
|
|
502
526
|
// Check for local override
|
|
503
527
|
if (!this.hasLocalOverride(path)) {
|
|
504
528
|
// Issue #1002: the manager's writable home (precedence 2 — above the
|
|
@@ -509,7 +533,7 @@ class LocalRegistryResolver {
|
|
|
509
533
|
// Issue #1867: apply inheritance so an extends-based override merges with its parent.
|
|
510
534
|
let resolvedManagerLocal;
|
|
511
535
|
try {
|
|
512
|
-
resolvedManagerLocal = await
|
|
536
|
+
resolvedManagerLocal = await resolveContent(managerLocalContent);
|
|
513
537
|
}
|
|
514
538
|
catch {
|
|
515
539
|
resolvedManagerLocal = { content: managerLocalContent, imports: [] };
|
|
@@ -529,7 +553,7 @@ class LocalRegistryResolver {
|
|
|
529
553
|
// Issue #1867: apply inheritance so an extends-based override merges with its parent.
|
|
530
554
|
let resolvedManagerOverlay;
|
|
531
555
|
try {
|
|
532
|
-
resolvedManagerOverlay = await
|
|
556
|
+
resolvedManagerOverlay = await resolveContent(managerOverlayContent);
|
|
533
557
|
}
|
|
534
558
|
catch {
|
|
535
559
|
resolvedManagerOverlay = { content: managerOverlayContent, imports: [] };
|
|
@@ -549,7 +573,7 @@ class LocalRegistryResolver {
|
|
|
549
573
|
// Issue #1867: apply inheritance so an extends-based override merges with its parent.
|
|
550
574
|
let resolvedOrgOverlay;
|
|
551
575
|
try {
|
|
552
|
-
resolvedOrgOverlay = await
|
|
576
|
+
resolvedOrgOverlay = await resolveContent(orgOverlayContent);
|
|
553
577
|
}
|
|
554
578
|
catch {
|
|
555
579
|
resolvedOrgOverlay = { content: orgOverlayContent, imports: [] };
|
|
@@ -566,32 +590,16 @@ class LocalRegistryResolver {
|
|
|
566
590
|
const syncedLocalContent = this.readSyncedLocalFile(path);
|
|
567
591
|
if (syncedLocalContent !== null) {
|
|
568
592
|
// Synced machine baseline, not personalized.
|
|
569
|
-
return
|
|
570
|
-
content: syncedLocalContent,
|
|
571
|
-
source: 'local',
|
|
572
|
-
personalized: false,
|
|
573
|
-
inherited: false
|
|
574
|
-
};
|
|
593
|
+
return baselineResult(syncedLocalContent, 'local');
|
|
575
594
|
}
|
|
576
595
|
const workspaceRegistryContent = this.readWorkspaceRegistryFile(path);
|
|
577
596
|
if (workspaceRegistryContent !== null) {
|
|
578
|
-
return
|
|
579
|
-
content: workspaceRegistryContent,
|
|
580
|
-
source: 'local',
|
|
581
|
-
personalized: false,
|
|
582
|
-
inherited: false
|
|
583
|
-
};
|
|
597
|
+
return baselineResult(workspaceRegistryContent, 'local');
|
|
584
598
|
}
|
|
585
599
|
// No useful override or synced content, fetch from remote
|
|
586
600
|
try {
|
|
587
601
|
const rawContent = await this.fetchRemoteWithFallback(path);
|
|
588
|
-
|
|
589
|
-
return {
|
|
590
|
-
content,
|
|
591
|
-
source: 'remote',
|
|
592
|
-
personalized: false,
|
|
593
|
-
inherited: false
|
|
594
|
-
};
|
|
602
|
+
return await baselineResult(rawContent, 'remote');
|
|
595
603
|
}
|
|
596
604
|
catch (error) {
|
|
597
605
|
throw new Error(`Failed to fetch from remote: ${path}. ${error.message}`);
|
|
@@ -610,28 +618,16 @@ class LocalRegistryResolver {
|
|
|
610
618
|
// If local read fails, fall back to remote
|
|
611
619
|
console.warn(`Local override read failed, falling back to remote: ${path}`);
|
|
612
620
|
const rawContent = await this.fetchRemoteWithFallback(path);
|
|
613
|
-
|
|
614
|
-
return {
|
|
615
|
-
content,
|
|
616
|
-
source: 'remote',
|
|
617
|
-
personalized: false,
|
|
618
|
-
inherited: false
|
|
619
|
-
};
|
|
621
|
+
return baselineResult(rawContent, 'remote');
|
|
620
622
|
}
|
|
621
623
|
// Resolve inheritance
|
|
622
624
|
let resolved;
|
|
623
625
|
try {
|
|
624
|
-
resolved = await
|
|
626
|
+
resolved = await resolveContent(localContent);
|
|
625
627
|
}
|
|
626
628
|
catch (error) {
|
|
627
629
|
const rawContent = await this.fetchRemoteWithFallback(path);
|
|
628
|
-
|
|
629
|
-
return {
|
|
630
|
-
content,
|
|
631
|
-
source: 'remote',
|
|
632
|
-
personalized: false,
|
|
633
|
-
inherited: false
|
|
634
|
-
};
|
|
630
|
+
return baselineResult(rawContent, 'remote');
|
|
635
631
|
}
|
|
636
632
|
// Build result — a local override hit means this came from the
|
|
637
633
|
// personalized-employee layer (the manager taught/customized it).
|
|
@@ -9,6 +9,7 @@ const path_1 = require("path");
|
|
|
9
9
|
const crypto_1 = __importDefault(require("crypto"));
|
|
10
10
|
const stub_generator_1 = require("../core/utils/stub-generator");
|
|
11
11
|
const job_parser_1 = require("../core/utils/job-parser");
|
|
12
|
+
const inheritance_parser_1 = require("../core/utils/inheritance-parser");
|
|
12
13
|
const pack_home_1 = require("../cli/utils/pack-home");
|
|
13
14
|
class RegistryService {
|
|
14
15
|
constructor() {
|
|
@@ -600,18 +601,38 @@ class RegistryService {
|
|
|
600
601
|
if (!metadata)
|
|
601
602
|
return null;
|
|
602
603
|
try {
|
|
603
|
-
return
|
|
604
|
+
return await this.resolveContent(metadata);
|
|
604
605
|
}
|
|
605
|
-
catch (
|
|
606
|
+
catch (error) {
|
|
607
|
+
if (error instanceof inheritance_parser_1.InheritanceError)
|
|
608
|
+
throw error;
|
|
606
609
|
return null;
|
|
607
610
|
}
|
|
608
611
|
}
|
|
612
|
+
/** Resolve shipped inheritance before parsing jobs or expanding includes. */
|
|
613
|
+
async resolveContent(metadata) {
|
|
614
|
+
const content = (0, fs_1.readFileSync)(metadata.fullPath, 'utf8');
|
|
615
|
+
const parser = new inheritance_parser_1.InheritanceParser();
|
|
616
|
+
if (!parser.parse(content).hasImports)
|
|
617
|
+
return content;
|
|
618
|
+
return parser.resolve(content, metadata.path, {
|
|
619
|
+
fetchParent: async (parentPath) => {
|
|
620
|
+
const parent = this.findFileByPath(parentPath);
|
|
621
|
+
if (!parent)
|
|
622
|
+
throw new Error(`Inherited registry file not found: ${parentPath}`);
|
|
623
|
+
// Leave recursion to InheritanceParser so cycles and depth are
|
|
624
|
+
// checked across the entire chain, rather than per fetch.
|
|
625
|
+
return (0, fs_1.readFileSync)(parent.fullPath, 'utf8');
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
}
|
|
609
629
|
async getJob(name) {
|
|
610
630
|
const allMatches = Array.from(this.fileIndex.values()).filter(f => f.name === name && f.type === 'job');
|
|
611
631
|
if (allMatches.length === 0)
|
|
612
632
|
return null;
|
|
613
633
|
const metadata = allMatches.find(f => !f.isStub) || allMatches[0];
|
|
614
|
-
const
|
|
634
|
+
const content = await this.resolveContent(metadata);
|
|
635
|
+
const def = job_parser_1.JobParser.parseContent(content, name, metadata.fullPath);
|
|
615
636
|
if (def && !def.metadata.type) {
|
|
616
637
|
def.metadata.type = 'job';
|
|
617
638
|
}
|