aios-core
Version:
Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework
170 lines (148 loc) • 5.17 kB
JavaScript
/**
* Greeting Preference Configuration Manager
*
* Manages user preferences for agent greeting personification levels.
* Integrates with GreetingBuilder (Story 6.1.2.5).
*
* Story ACT-2: Now accounts for user_profile — bob mode forces minimal/named.
*
* Performance: Preference check <5ms (called on every agent activation)
*/
const fs = require('fs');
const path = require('path');
const yaml = require('js-yaml');
const CONFIG_PATH = path.join(process.cwd(), '.aios-core', 'core-config.yaml');
const BACKUP_PATH = path.join(process.cwd(), '.aios-core', 'core-config.yaml.backup');
const VALID_PREFERENCES = ['auto', 'minimal', 'named', 'archetypal'];
// Bob mode restricts greeting to simpler levels (Story ACT-2)
const BOB_MODE_ALLOWED_PREFERENCES = ['minimal', 'named'];
const BOB_MODE_DEFAULT_PREFERENCE = 'named';
class GreetingPreferenceManager {
/**
* Get current greeting preference, accounting for user_profile restrictions.
*
* Story ACT-2 - AC1: When user_profile === 'bob', forces preference to
* 'minimal' or 'named' regardless of what is configured. If the configured
* preference is 'auto' or 'archetypal', it falls back to BOB_MODE_DEFAULT_PREFERENCE.
*
* @param {string} [userProfile] - Optional user_profile override. If not provided, reads from config.
* @returns {string} Current preference (auto|minimal|named|archetypal)
*/
getPreference(userProfile) {
try {
const config = this._loadConfig();
const rawPreference = config?.agentIdentity?.greeting?.preference || 'auto';
// Story ACT-2: If bob mode, restrict preference to minimal/named
const effectiveProfile = userProfile || config?.user_profile;
if (effectiveProfile === 'bob') {
if (BOB_MODE_ALLOWED_PREFERENCES.includes(rawPreference)) {
return rawPreference;
}
// Override non-allowed preferences (auto, archetypal) to bob default
return BOB_MODE_DEFAULT_PREFERENCE;
}
return rawPreference;
} catch (error) {
console.warn('[GreetingPreference] Failed to load, using default:', error.message);
return 'auto';
}
}
/**
* Set greeting preference
* @param {string} preference - New preference (auto|minimal|named|archetypal)
* @throws {Error} If preference is invalid
*/
setPreference(preference) {
// Validate preference
if (!VALID_PREFERENCES.includes(preference)) {
const validOptions = VALID_PREFERENCES.join(', ');
throw new Error(
`Invalid preference: "${preference}". ` +
`Valid options: ${validOptions}. ` +
'Examples: "auto" (session-aware), "minimal" (always minimal), "named" (always named), "archetypal" (always archetypal)',
);
}
try {
// Backup existing config before modification
this._backupConfig();
const config = this._loadConfig();
// Ensure structure exists
if (!config.agentIdentity) config.agentIdentity = {};
if (!config.agentIdentity.greeting) config.agentIdentity.greeting = {};
// Set preference
config.agentIdentity.greeting.preference = preference;
// Validate YAML before write
const yamlContent = yaml.dump(config, { lineWidth: -1 });
try {
yaml.load(yamlContent); // Validate syntax
} catch (yamlError) {
this._restoreBackup();
throw new Error(`Invalid YAML generated: ${yamlError.message}`);
}
// Write back
this._saveConfig(config);
return { success: true, preference };
} catch (error) {
// Restore backup on error
this._restoreBackup();
throw new Error(`Failed to set preference: ${error.message}`);
}
}
/**
* Get all greeting configuration
* @returns {Object} Complete greeting config
*/
getConfig() {
try {
const config = this._loadConfig();
return config?.agentIdentity?.greeting || {};
} catch (error) {
console.warn('[GreetingPreference] Failed to load config, returning empty:', error.message);
return {};
}
}
/**
* Backup config file before modification
* @private
*/
_backupConfig() {
try {
if (fs.existsSync(CONFIG_PATH)) {
fs.copyFileSync(CONFIG_PATH, BACKUP_PATH);
}
} catch (error) {
console.warn('[GreetingPreference] Failed to backup config:', error.message);
}
}
/**
* Restore config from backup
* @private
*/
_restoreBackup() {
try {
if (fs.existsSync(BACKUP_PATH)) {
fs.copyFileSync(BACKUP_PATH, CONFIG_PATH);
console.log('[GreetingPreference] Config restored from backup');
}
} catch (error) {
console.error('[GreetingPreference] Failed to restore backup:', error.message);
}
}
/**
* Load config from YAML
* @private
*/
_loadConfig() {
const content = fs.readFileSync(CONFIG_PATH, 'utf8');
return yaml.load(content);
}
/**
* Save config to YAML
* @private
*/
_saveConfig(config) {
const content = yaml.dump(config, { lineWidth: -1 });
fs.writeFileSync(CONFIG_PATH, content, 'utf8');
}
}
module.exports = GreetingPreferenceManager;