references.ts4.5 KBView on GitHub
import {
  Database,
  ListChecks,
  BookOpen,
  FileText,
  Building2,
  Bot,
  AtSign,
  type LucideIcon,
} from 'lucide-react';

/**
 * Playbook `@` reference namespaces (see apps/mail/docs/playbook-doc.md). The
 * namespace is the path prefix; it determines the chip's icon, accent, and how
 * the orchestrator would treat the reference. `crm-updater` / `next-steps` are
 * the two reserved system tokens.
 */
export type ReferenceNamespace =
  | 'crm-updater'
  | 'next-steps'
  | 'knowledge-base'
  | 'resources'
  | 'org'
  | 'subagents'
  | 'other';

interface NamespaceMeta {
  icon: LucideIcon;
  /** Tailwind classes for the chip — system tokens get a stronger accent. */
  className: string;
  isSystemToken=[redacted];
}

const NAMESPACE_META: Record<ReferenceNamespace, NamespaceMeta> = {
  'crm-updater': {
    icon: Database,
    className: 'bg-blue-500/10 text-blue-600 dark:text-blue-400',
    isSystemToken=[redacted],
  },
  'next-steps': {
    icon: ListChecks,
    className: 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400',
    isSystemToken=[redacted],
  },
  'knowledge-base': {
    icon: BookOpen,
    className: 'bg-muted text-foreground/90',
    isSystemToken=[redacted],
  },
  resources: { icon: FileText, className: 'bg-muted text-foreground/90', isSystemToken=[redacted] },
  org: { icon: Building2, className: 'bg-muted text-foreground/90', isSystemToken=[redacted] },
  subagents: {
    icon: Bot,
    className: 'bg-purple-500/10 text-purple-600 dark:text-purple-400',
    isSystemToken=[redacted],
  },
  other: { icon: AtSign, className: 'bg-muted text-foreground/90', isSystemToken=[redacted] },
};

export function namespaceForPath(path: string): ReferenceNamespace {
  const head = path.split('/')[0];
  if (head === 'crm-updater' || head === 'next-steps') return head;
  if (head === 'knowledge-base' || head === 'resources' || head === 'org' || head === 'subagents') {
    return head;
  }
  return 'other';
}

export function referenceMeta(path: string): NamespaceMeta {
  return NAMESPACE_META[namespaceForPath(path)];
}

/**
 * System tokens (`@crm-updater`, `@next-steps`) are live UI blocks, not documents
 * — everything else resolves to an openable doc.
 */
export function isOpenableReference(path: string): boolean {
  return !referenceMeta(path).isSystemToken;
}

/**
 * Maps a reference path to the virtual document path it opens. The `#section`
 * anchor is dropped (the whole doc is opened). All playbook references resolve
 * under the user scope so they find-or-create as editable user docs.
 *   `resources/templates#discovery-post-demo` → `user/playbooks/resources/templates`
 */
export function resolveReferencePath(path: string): string {
  const withoutAnchor = path.split('#')[0];
  return `user/playbooks/${withoutAnchor}`;
}

/** The `@…` label shown in the chip, trimmed of any `#section` anchor for brevity. */
export function referenceLabel(path: string): string {
  return `@${path}`;
}

export interface ReferenceOption {
  path: string;
  /** Short human description shown in the `@` menu. */
  description: string;
  /**
   * The document this path resolves to, when the caller knows it.
   *
   * Absent for the system tokens (`@crm-updater`, `@next-steps` — live UI blocks, not
   * documents) and for the static {@link REFERENCE_OPTIONS} fallbacks, which are paths
   * nobody has resolved. A `<ref>` is addressed by id, so only an option that carries one
   * can become a `triggerRef` panel when picked inside a trigger.
   */
  documentId?: string;
}

/** Canonical options surfaced by the `@` menu, in display order. */
export const REFERENCE_OPTIONS: ReferenceOption[] = [
  { path: 'crm-updater', description: 'System token · manage conversation fields' },
  { path: 'next-steps', description: 'System token · manage tasks & next step' },
  { path: 'knowledge-base', description: 'On-demand reference corpus' },
  { path: 'knowledge-base/company-background', description: 'Company background doc' },
  { path: 'resources/overall-goal', description: 'Always-loaded AOP goal' },
  { path: 'resources/email-style', description: 'Email style guide' },
  { path: 'resources/templates', description: 'Template library (by anchor)' },
  { path: 'resources/coaching-framework', description: 'Coaching framework' },
  { path: 'resources/discovery-guide', description: 'Discovery guide' },
  { path: 'org/deals-playbook', description: 'Org-level Deals playbook' },
  { path: 'subagents/bant-coach', description: 'Custom subagent' },
  { path: 'subagents/notify-solutions-engineer', description: 'Custom subagent' },
];