Governed substrate for autonomous agents: scoped identity (passports), audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
132 lines
4.5 KiB
JavaScript
132 lines
4.5 KiB
JavaScript
// ============================================================
|
|
// CRM Bridge for PES — Polls CRM events and triggers PES
|
|
// ============================================================
|
|
// This module runs inside PES process and polls crm-events.json
|
|
// When events are found, it triggers PES emotional responses
|
|
// ============================================================
|
|
|
|
import { readFileSync, writeFileSync, existsSync, renameSync } from 'node:fs';
|
|
import { join, dirname } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const EVENTS_FILE = join(__dirname, '../pes-data/crm-events.json');
|
|
|
|
// CRM event type → PES trigger mapping
|
|
const EVENT_MAPPING = {
|
|
ticket_created: { trigger: 'agent_active', mood: -0.02, energy: +0.05, curiosity: +0.1 },
|
|
ticket_resolved: { trigger: 'owner_praise', mood: +0.1, energy: +0.05, curiosity: 0 },
|
|
agent_completed: { trigger: 'command_learned', mood: +0.05, energy: +0.02, curiosity: +0.05 },
|
|
agent_failed: { trigger: 'bug_detected', mood: -0.05, energy: -0.03, curiosity: +0.1 },
|
|
deployment_success: { trigger: 'owner_praise', mood: +0.15, energy: +0.1, curiosity: 0 },
|
|
deployment_failed: { trigger: 'bug_detected', mood: -0.1, energy: -0.05, curiosity: +0.15 },
|
|
user_login: { trigger: 'owner_returned', mood: +0.1, energy: +0.1, curiosity: +0.05 },
|
|
user_logout: { trigger: 'owner_left', mood: -0.05, energy: -0.02, curiosity: -0.05 },
|
|
error_spike: { trigger: 'anomaly_pattern', mood: -0.1, energy: +0.1, curiosity: +0.2 },
|
|
custom: { trigger: 'owner_message', mood: 0, energy: 0, curiosity: +0.05 },
|
|
};
|
|
|
|
export class CrmBridge {
|
|
/**
|
|
* @param {import('../core/pes.js').default} pes — PES instance
|
|
* @param {Object} opts
|
|
* @param {number} [opts.pollInterval=10000] — ms between polls
|
|
* @param {Function} [opts.onEvent] — callback when event processed
|
|
*/
|
|
constructor(pes, opts = {}) {
|
|
this.pes = pes;
|
|
this.pollInterval = opts.pollInterval || 10_000;
|
|
this.onEvent = opts.onEvent || null;
|
|
this._timer = null;
|
|
this._running = false;
|
|
}
|
|
|
|
start() {
|
|
if (this._running) return;
|
|
this._running = true;
|
|
this._poll(); // immediate first poll
|
|
this._timer = setInterval(() => this._poll(), this.pollInterval);
|
|
console.log(`[CRM-BRIDGE] Started polling CRM events every ${this.pollInterval}ms`);
|
|
}
|
|
|
|
stop() {
|
|
this._running = false;
|
|
if (this._timer) {
|
|
clearInterval(this._timer);
|
|
this._timer = null;
|
|
}
|
|
console.log('[CRM-BRIDGE] Stopped');
|
|
}
|
|
|
|
_poll() {
|
|
try {
|
|
if (!existsSync(EVENTS_FILE)) return;
|
|
|
|
const raw = readFileSync(EVENTS_FILE, 'utf8');
|
|
const events = JSON.parse(raw);
|
|
const pending = events.filter(e => !e.consumed);
|
|
|
|
if (pending.length === 0) return;
|
|
|
|
const consumedIds = [];
|
|
|
|
for (const event of pending) {
|
|
try {
|
|
this._processEvent(event);
|
|
consumedIds.push(event.id);
|
|
} catch (err) {
|
|
console.error(`[CRM-BRIDGE] Error processing event ${event.id}:`, err.message);
|
|
}
|
|
}
|
|
|
|
// Mark consumed
|
|
if (consumedIds.length > 0) {
|
|
for (const e of events) {
|
|
if (consumedIds.includes(e.id)) e.consumed = true;
|
|
}
|
|
const tmp = EVENTS_FILE + '.tmp';
|
|
writeFileSync(tmp, JSON.stringify(events, null, 2));
|
|
renameSync(tmp, EVENTS_FILE);
|
|
console.log(`[CRM-BRIDGE] Processed ${consumedIds.length} CRM events`);
|
|
}
|
|
} catch (err) {
|
|
console.error('[CRM-BRIDGE] Event poll error:', err.message);
|
|
}
|
|
}
|
|
|
|
_processEvent(event) {
|
|
const mapping = EVENT_MAPPING[event.type];
|
|
if (!mapping) {
|
|
console.log(`[CRM-BRIDGE] Unknown event type: ${event.type}`);
|
|
return;
|
|
}
|
|
|
|
// Trigger PES event
|
|
if (this.pes && this.pes.event) {
|
|
const pesEvent = {
|
|
type: mapping.trigger,
|
|
source: 'crm',
|
|
detail: event.data,
|
|
timestamp: event.timestamp,
|
|
vitalAdjustments: {
|
|
mood: mapping.mood,
|
|
energy: mapping.energy,
|
|
curiosity: mapping.curiosity,
|
|
},
|
|
};
|
|
|
|
this.pes.event(pesEvent);
|
|
|
|
console.log(
|
|
`[CRM-BRIDGE] ${event.type} → PES trigger:${mapping.trigger}`,
|
|
`(mood:${mapping.mood > 0 ? '+' : ''}${mapping.mood}, energy:${mapping.energy > 0 ? '+' : ''}${mapping.energy})`
|
|
);
|
|
}
|
|
|
|
if (this.onEvent) {
|
|
this.onEvent(event, mapping);
|
|
}
|
|
}
|
|
}
|
|
|
|
export default CrmBridge;
|