{
  "schema_version": 1,
  "diagram_type": "architecture",
  "meta": {
    "title": "Agent Studio and Platform Services",
    "subtitle": "Authoring control plane, shared tenant state, the conversation runtimes, and the telephony edge that feeds them",
    "output": "docs/02-architecture/agent-studio-architecture.html",
    "visual_preset": "blueprint",
    "quality_profile": "showcase",
    "viewBox": [
      1580,
      960
    ],
    "repository": {
      "url": "https://github.com/Deloitte-US/trueserve-framework",
      "revision": "4e869d5aa9e652d509348305ca7fc47f47fa84d9"
    },
    "views": [
      {
        "id": "authoring",
        "label": "Authoring a draft",
        "focus": [
          "authors",
          "studio",
          "collab",
          "postgres"
        ],
        "note": "Editor edits become a Y.Doc, flush back through ai-manager, and land on agent_versions.config."
      },
      {
        "id": "ai-authoring",
        "label": "AI and external authoring",
        "focus": [
          "devtools",
          "studio",
          "runner",
          "postgres"
        ],
        "note": "Generate with AI and the editor Assistant run a sandboxed CLI that writes back over scoped MCP."
      },
      {
        "id": "publish",
        "label": "Publish to runtime",
        "focus": [
          "postgres",
          "orchestrator",
          "asyncagent",
          "convapi",
          "surface"
        ],
        "note": "Runtimes resolve the published version from Postgres; Studio never pushes config to them."
      },
      {
        "id": "voice-edge",
        "label": "A phone call arrives",
        "focus": [
          "callers",
          "sipedge",
          "aricontroller",
          "mediaproxy",
          "ccaas",
          "orchestrator"
        ],
        "note": "SIP lands on Kamailio/Asterisk; ari-controller allocates media-proxy legs; CCaaS connectors stream straight to the orchestrator."
      }
    ]
  },
  "components": [
    {
      "id": "sso",
      "type": "security",
      "label": "Authentik SSO",
      "sublabel": "group claims authoritative",
      "tag": "IdP",
      "pos": [
        40,
        90
      ],
      "size": [
        148,
        64
      ],
      "sources": [
        {
          "path": "apps/ai-manager/lib/rbac/local-rbac-service.ts",
          "label": "syncTenantMemberships"
        },
        {
          "path": "apps/ai-manager/lib/authentik/groups.ts",
          "label": "tenant:<slug>:<role>"
        }
      ]
    },
    {
      "id": "authors",
      "type": "external",
      "label": "Studio Users",
      "sublabel": "SSO browser session",
      "tag": "admin / developer",
      "pos": [
        40,
        300
      ],
      "size": [
        148,
        64
      ]
    },
    {
      "id": "devtools",
      "type": "external",
      "label": "Claude Code / Codex",
      "sublabel": "external authoring client",
      "tag": "OAuth public client",
      "pos": [
        40,
        520
      ],
      "size": [
        152,
        64
      ],
      "sources": [
        {
          "path": "docs/04-processes/external-authoring-mcp.md",
          "label": "authoring surface"
        }
      ]
    },
    {
      "id": "studio",
      "type": "frontend",
      "label": "Agent Studio",
      "sublabel": "ai-manager \u2014 UI, BFF, MCP",
      "tag": "Next.js :3001",
      "pos": [
        270,
        288
      ],
      "size": [
        168,
        84
      ],
      "sources": [
        {
          "path": "apps/ai-manager/app/(main)/agents/[agentId]/page.tsx",
          "label": "agent editor"
        },
        {
          "path": "apps/ai-manager/app/api/v1/management/agents/route.ts",
          "label": "management REST"
        },
        {
          "path": "apps/ai-manager/app/api/mcp/authoring/route.ts",
          "label": "authoring MCP"
        }
      ]
    },
    {
      "id": "collab",
      "type": "backend",
      "label": "collab-gateway",
      "sublabel": "presence + Yjs allow-list",
      "tag": "Go + Node :3510",
      "pos": [
        540,
        70
      ],
      "size": [
        152,
        64
      ],
      "sources": [
        {
          "path": "apps/collab-gateway/src/shape/pipeline-shape.ts",
          "label": "agent config shape"
        },
        {
          "path": "apps/collab-gateway/src/shape/workflow-shape.ts",
          "label": "workflow config shape"
        }
      ]
    },
    {
      "id": "runner",
      "type": "backend",
      "label": "workspace-agent-runner",
      "sublabel": "sandboxed authoring CLI",
      "tag": "FastAPI :8012",
      "pos": [
        262,
        520
      ],
      "size": [
        184,
        64
      ],
      "sources": [
        {
          "path": "apps/workspace-agent-runner/app/generation.py",
          "label": "Generate with AI"
        },
        {
          "path": "apps/workspace-agent-runner/app/assistant.py",
          "label": "editor Assistant"
        },
        {
          "path": "apps/workspace-agent-runner/app/sandbox.py",
          "label": "sandbox boundary"
        }
      ]
    },
    {
      "id": "catalog",
      "type": "backend",
      "label": "catalog-builder",
      "sublabel": "A2UI compile + publish",
      "tag": "Node :8090",
      "pos": [
        560,
        470
      ],
      "size": [
        152,
        64
      ],
      "sources": [
        {
          "path": "apps/catalog-builder/src/index.ts",
          "label": "bundle compiler"
        }
      ]
    },
    {
      "id": "redis",
      "type": "database",
      "label": "Redis",
      "sublabel": "presence, doc blobs, streams",
      "tag": "DB 4 presence",
      "pos": [
        810,
        70
      ],
      "size": [
        152,
        64
      ]
    },
    {
      "id": "postgres",
      "type": "database",
      "label": "PostgreSQL",
      "sublabel": "agent_versions.config, tenant RLS",
      "tag": "squashed baseline",
      "pos": [
        810,
        288
      ],
      "size": [
        152,
        72
      ],
      "sources": [
        {
          "path": "deploy/init-scripts/000_full_schema.sql",
          "label": "canonical baseline"
        },
        {
          "path": "apps/ai-manager/lib/db.ts",
          "label": "withTenantContext"
        }
      ]
    },
    {
      "id": "orchestrator",
      "type": "backend",
      "label": "ai-orchestrator",
      "sublabel": "voice / S2S pipeline, preflight",
      "tag": "Pipecat :50051",
      "pos": [
        1050,
        472
      ],
      "size": [
        162,
        68
      ],
      "sources": [
        {
          "path": "apps/ai-orchestrator/lib/pipecat/pipeline/db_pipeline_store.py",
          "label": "resolve agent version"
        },
        {
          "path": "apps/ai-orchestrator/lib/pipecat/pipeline/preflight.py",
          "label": "buildability gate"
        }
      ]
    },
    {
      "id": "convapi",
      "type": "backend",
      "label": "conversation-api",
      "sublabel": "sessions, transcripts, SSE",
      "tag": "Python :8080",
      "pos": [
        1050,
        296
      ],
      "size": [
        162,
        68
      ],
      "sources": [
        {
          "path": "apps/conversation-api/main.py",
          "label": "event broker"
        }
      ]
    },
    {
      "id": "asyncagent",
      "type": "backend",
      "label": "async-agent",
      "sublabel": "triggered workflow executor",
      "tag": "LangGraph :8000",
      "pos": [
        1050,
        120
      ],
      "size": [
        162,
        68
      ],
      "sources": [
        {
          "path": "apps/async-agent/app/workers/workflow_fanout_worker.py",
          "label": "workflow fan-out"
        }
      ]
    },
    {
      "id": "surface",
      "type": "frontend",
      "label": "surface-app",
      "sublabel": "the one embeddable UI",
      "tag": "Next.js :3002",
      "pos": [
        1300,
        296
      ],
      "size": [
        156,
        68
      ],
      "sources": [
        {
          "path": "apps/surface-app/app/[locale]/page.tsx",
          "label": "voice + chat surface"
        }
      ]
    },
    {
      "id": "callers",
      "type": "external",
      "label": "Callers",
      "sublabel": "PSTN + CCaaS platforms",
      "tag": "SIP / streams",
      "pos": [
        40,
        700
      ],
      "size": [
        148,
        64
      ]
    },
    {
      "id": "sipedge",
      "type": "cloud",
      "label": "SIP Edge",
      "sublabel": "Kamailio + Asterisk",
      "tag": "SIP :5060/:5061",
      "pos": [
        270,
        700
      ],
      "size": [
        160,
        68
      ],
      "sources": [
        {
          "path": "docs/03-services/telephony-stack/kamailio.md",
          "label": "SIP routing + TLS"
        },
        {
          "path": "docs/03-services/telephony-stack/asterisk.md",
          "label": "PJSIP + ARI dialplan"
        }
      ]
    },
    {
      "id": "aricontroller",
      "type": "backend",
      "label": "ari-controller",
      "sublabel": "call control, CCaaS gate",
      "tag": "Node :3000",
      "pos": [
        540,
        700
      ],
      "size": [
        160,
        68
      ],
      "sources": [
        {
          "path": "apps/ari-controller-node/src/index.ts",
          "label": "ARI bridge"
        }
      ]
    },
    {
      "id": "mediaproxy",
      "type": "backend",
      "label": "media-proxy",
      "sublabel": "RTP / WebRTC fan-in, mixing",
      "tag": "Go :8085 / :18080",
      "pos": [
        810,
        700
      ],
      "size": [
        160,
        68
      ],
      "sources": [
        {
          "path": "apps/media-proxy/internal/proxy/allocator.go",
          "label": "session allocator"
        },
        {
          "path": "apps/media-proxy/cmd/media-proxy/main.go",
          "label": "media pipeline"
        }
      ]
    },
    {
      "id": "ccaas",
      "type": "backend",
      "label": "CCaaS Connectors",
      "sublabel": "Genesys, Connect, Five9, Webex, SCV",
      "tag": "Go / Node",
      "pos": [
        1300,
        700
      ],
      "size": [
        160,
        68
      ],
      "sources": [
        {
          "path": "apps/genesys-connector-go/cmd/genesys-connector/main.go",
          "label": "AudioHook terminator"
        },
        {
          "path": "docs/03-services/integrations/genesys-connector-go.md",
          "label": "connector catalog"
        }
      ]
    }
  ],
  "boundaries": [
    {
      "kind": "security-group",
      "label": "authoring control plane",
      "wraps": [
        "studio",
        "collab",
        "runner",
        "catalog"
      ]
    },
    {
      "kind": "region",
      "label": "shared tenant state",
      "wraps": [
        "redis",
        "postgres"
      ]
    },
    {
      "kind": "security-group",
      "label": "conversation runtime plane",
      "wraps": [
        "orchestrator",
        "convapi",
        "asyncagent"
      ]
    },
    {
      "kind": "security-group",
      "label": "telephony edge",
      "wraps": [
        "sipedge",
        "aricontroller",
        "mediaproxy",
        "ccaas"
      ]
    }
  ],
  "connections": [
    {
      "from": "authors",
      "to": "studio",
      "variant": "emphasis"
    },
    {
      "from": "devtools",
      "to": "studio",
      "label": "OAuth PKCE + MCP",
      "variant": "security"
    },
    {
      "from": "studio",
      "to": "sso",
      "label": "tenant + admin claims",
      "variant": "security"
    },
    {
      "from": "studio",
      "to": "collab",
      "label": "Y.Doc autosave",
      "fromSide": "top",
      "toSide": "left"
    },
    {
      "from": "studio",
      "to": "runner",
      "label": "dispatch job",
      "variant": "emphasis",
      "labelAt": [
        296,
        452
      ]
    },
    {
      "from": "runner",
      "to": "studio",
      "label": "MCP save_draft",
      "variant": "dashed",
      "labelAt": [
        452,
        452
      ]
    },
    {
      "from": "studio",
      "to": "catalog",
      "label": "publish catalog",
      "labelAt": [
        575,
        420
      ]
    },
    {
      "from": "studio",
      "to": "postgres",
      "label": "draft + publish",
      "variant": "emphasis",
      "labelAt": [
        750,
        306
      ]
    },
    {
      "from": "collab",
      "to": "redis",
      "label": "presence fan-out",
      "variant": "dashed"
    },
    {
      "from": "catalog",
      "to": "postgres",
      "label": "catalog rows"
    },
    {
      "from": "postgres",
      "to": "orchestrator",
      "label": "resolve version",
      "variant": "emphasis"
    },
    {
      "from": "postgres",
      "to": "asyncagent",
      "label": "trigger rules"
    },
    {
      "from": "orchestrator",
      "to": "convapi",
      "label": "transcripts + events",
      "variant": "emphasis"
    },
    {
      "from": "asyncagent",
      "to": "convapi",
      "label": "run events",
      "labelDy": 24
    },
    {
      "from": "convapi",
      "to": "surface",
      "label": "SSE",
      "variant": "emphasis"
    },
    {
      "from": "callers",
      "to": "sipedge",
      "label": "SIP + RTP",
      "variant": "emphasis",
      "labelDy": -35
    },
    {
      "from": "callers",
      "to": "ccaas",
      "label": "vendor media streams",
      "variant": "dashed",
      "fromSide": "bottom",
      "toSide": "bottom"
    },
    {
      "from": "sipedge",
      "to": "aricontroller",
      "label": "ARI events",
      "variant": "emphasis"
    },
    {
      "from": "aricontroller",
      "to": "mediaproxy",
      "label": "allocate media legs",
      "variant": "emphasis",
      "labelAt": [
        724,
        782
      ]
    },
    {
      "from": "aricontroller",
      "to": "postgres",
      "label": "CCaaS allow-list",
      "variant": "security",
      "fromSide": "top",
      "toSide": "bottom",
      "labelAt": [
        620,
        560
      ]
    },
    {
      "from": "mediaproxy",
      "to": "orchestrator",
      "label": "gRPC audio",
      "variant": "emphasis"
    },
    {
      "from": "ccaas",
      "to": "orchestrator",
      "label": "session gRPC",
      "variant": "emphasis"
    }
  ],
  "cards": [
    {
      "dot": "cyan",
      "title": "Two Authoring Doorways",
      "items": [
        "Browser editors autosave through collab-gateway, which flushes config back to ai-manager and keeps durable Yjs state in collab_documents",
        "The Yjs converters are a fixed allow-list, so an unlisted top-level config key is silently dropped on every flush",
        "External clients use versioned REST and stateless MCP with fine-grained management scopes, never the platform-admin bypass"
      ]
    },
    {
      "dot": "violet",
      "title": "AI Authoring Is Sandboxed",
      "items": [
        "Generate with AI and the editor Assistant both run a scoped CLI inside workspace-agent-runner",
        "The sandbox writes only through the generation MCP endpoint using a per-job token bound to one target",
        "Validation is two tiers: structural JSON Schema in Studio, buildability preflight in ai-orchestrator"
      ]
    },
    {
      "dot": "emerald",
      "title": "Publish Is A Database Fact",
      "items": [
        "Runtimes resolve the published agent_versions row themselves; Studio pushes no config to them",
        "Tenant isolation is Postgres RLS, while Authentik groups stay authoritative for membership and platform admin",
        "Not drawn: MinIO catalog bundles, CRM embeds, OTLP telemetry"
      ]
    },
    {
      "dot": "amber",
      "title": "Telephony Edge",
      "items": [
        "Inbound CCaaS is default-deny: ari-controller rejects calls without an enabled ccaas_pipeline_integrations row",
        "SIP calls land on Kamailio/Asterisk; ari-controller allocates media-proxy legs that stream gRPC audio to ai-orchestrator",
        "Janus, rtpengine, and coturn (WebRTC/TURN media plane) are folded into the SIP edge here"
      ]
    }
  ]
}
