← back to Dw Boardroom Governance

src/engine/delegationEngine.ts

149 lines

/**
 * Delegation Engine — Keyword-based task routing + contract management
 * Ported from /root/DW-Agents/boardroom-agent/lib/delegation-engine.js
 *
 * Features:
 *   - TASK_KEYWORDS map for intelligent routing
 *   - EXEC_DELEGATES hierarchy for fallback
 *   - Contract lifecycle (create, complete, revoke, expire)
 */

import { query, queryOne, execute } from '../db/db';
import { broadcast } from '../ws/broadcast';
import { DelegationContract, TASK_KEYWORDS, EXEC_DELEGATES } from '../types';
import { v4 as uuidv4 } from 'uuid';

// ─── Keyword Routing ────────────────────────────

export function routeByKeywords(description: string): string | null {
  const lower = description.toLowerCase();

  // Check multi-word keywords first (more specific)
  const multiWordKeys = Object.keys(TASK_KEYWORDS).filter(k => k.includes(' '));
  for (const keyword of multiWordKeys) {
    if (lower.includes(keyword)) {
      return TASK_KEYWORDS[keyword];
    }
  }

  // Then single-word keywords
  const words = lower.split(/\s+/);
  for (const word of words) {
    if (TASK_KEYWORDS[word]) {
      return TASK_KEYWORDS[word];
    }
  }

  return null;
}

export function getExecForAgent(agentId: string): string | null {
  for (const [exec, agents] of Object.entries(EXEC_DELEGATES)) {
    if (agents.includes(agentId)) return exec;
  }
  return null;
}

export function buildHierarchyPath(agentId: string): string {
  const exec = getExecForAgent(agentId);
  if (!exec) return agentId;
  return `${exec} > ${agentId}`;
}

// ─── Contract CRUD ──────────────────────────────

export async function createDelegation(data: {
  decisionId?: string;
  owner: string;
  delegate: string;
  autonomyLevel?: number;
  metric?: string;
  checkpointDate?: string;
  routingKeywords?: string[];
}): Promise<DelegationContract> {
  const id = uuidv4();
  const hierarchyPath = buildHierarchyPath(data.delegate);

  await query(
    `INSERT INTO gov_delegation_contract
     (id, decision_id, owner, delegate, autonomy_level, metric, checkpoint_date, routing_keywords, hierarchy_path)
     VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)`,
    [
      id,
      data.decisionId || null,
      data.owner,
      data.delegate,
      data.autonomyLevel || 1,
      data.metric || null,
      data.checkpointDate || null,
      data.routingKeywords || null,
      hierarchyPath,
    ]
  );

  const contract = (await queryOne<DelegationContract>(
    'SELECT * FROM gov_delegation_contract WHERE id = $1', [id]
  ))!;

  broadcast('delegation_created', contract);
  console.log(`[Delegation] Created: ${data.owner} → ${data.delegate} (${hierarchyPath})`);

  return contract;
}

export async function completeDelegation(id: string): Promise<DelegationContract> {
  await execute(
    "UPDATE gov_delegation_contract SET status = 'completed', completed_at = NOW() WHERE id = $1",
    [id]
  );
  const contract = await queryOne<DelegationContract>(
    'SELECT * FROM gov_delegation_contract WHERE id = $1', [id]
  );
  if (!contract) throw new Error('Delegation not found');
  return contract;
}

export async function revokeDelegation(id: string): Promise<DelegationContract> {
  await execute(
    "UPDATE gov_delegation_contract SET status = 'revoked', completed_at = NOW() WHERE id = $1",
    [id]
  );
  const contract = await queryOne<DelegationContract>(
    'SELECT * FROM gov_delegation_contract WHERE id = $1', [id]
  );
  if (!contract) throw new Error('Delegation not found');
  return contract;
}

export async function listDelegations(status?: string): Promise<DelegationContract[]> {
  if (status) {
    return query<DelegationContract>(
      'SELECT * FROM gov_delegation_contract WHERE status = $1 ORDER BY created_at DESC',
      [status]
    );
  }
  return query<DelegationContract>(
    'SELECT * FROM gov_delegation_contract ORDER BY created_at DESC LIMIT 50'
  );
}

export async function getDelegation(id: string): Promise<DelegationContract | null> {
  return queryOne<DelegationContract>(
    'SELECT * FROM gov_delegation_contract WHERE id = $1', [id]
  );
}

export async function checkExpiredContracts(): Promise<number> {
  const result = await execute(
    `UPDATE gov_delegation_contract
     SET status = 'expired', completed_at = NOW()
     WHERE status = 'active'
     AND checkpoint_date IS NOT NULL
     AND checkpoint_date < NOW()`
  );
  if (result > 0) {
    console.log(`[Delegation] Expired ${result} contract(s)`);
  }
  return result;
}