Skip to main content

Architecture and Package Boundaries

Target system

Monorepo

agent-platform/
packages/
core/ schemas/ models/ agents/ runtime/ tools/ mcp/ skills/ context/
authorization/ hitl/ persistence/ knowledge/ rag/ files/ documents/
pdf/ vision/ ocr/ artifacts/ usage/ evals/ graphql/ react/ ui/ testing/
adapters/
memory/ postgres/ supabase/ pgvector/ qdrant/ s3/ local-files/
bullmq/ redis/ nextjs/
integrations/shareflow/
examples/
docs/

Dependency rules

  • core imports no infrastructure, UI, application or provider package.
  • Runtime packages may depend on core contracts, but never ShareFlow domain code.
  • Adapter packages implement ports; ports never import adapters.
  • Imported MCP tools flow through the same authorization and approval path as native tools.
  • Authorization is a port; services take decisions from it and never inline permission logic.
  • Contracts, events and tool results carry stable codes and structured data — never pre-localized user prose. The frontend localizes (see docs/14).
  • react is headless. ui depends on react, not vice versa.
  • GraphQL resolvers call application services and contain no domain logic.
  • ShareFlow registers tools, context providers, skills and agents through public interfaces.
  • No public API contains Twenty names or types.
  • A generic package never imports an integration package. The reverse is the only allowed direction.
  • An integration package declares the service interfaces it needs and never imports the application that implements them, so it carries no dependency on that application's internals.
  • Every package may import only what its own manifest declares. Workspace hoisting makes an undeclared import work locally and fail once the package is installed on its own, so the manifest is where "builds without ShareFlow or Twenty installed" is actually verifiable.

packages/scripts/check-boundaries.mjs enforces these as build failures (rules R1–R10); each has a planted-violation test beside it.

Naming conventions

One rule per kind of name, applied everywhere. Deviations fail review.

KindConventionExample
npm packageskebab-case under the @forge scope@forge/runtime
Types and interfacesPascalCaseExecutionContext, ToolProvider
Branded ID typesPascalCase ending IdConversationId, RunId
Fields, variables, functionscamelCasetenantId, createAgentPlatform
Tool names (function-calling)snake_case, verb_nouncreate_post, read_tool_output
Meta-toolssnake_case, same rulelearn_tools, request_approval
Imported MCP toolsmcp__<serverId>__<toolName>mcp__acme__search
Union / enum string memberskebab-caseexternal-write, tool-call, streamable-http
Transport event typesdotted domain.event, lower-caserun.completed, tool.failed
Agent and skill IDskebab-case, stable, versioned by integerassistant (version 3)
Agent display nameshuman-readable Title CaseSocial Assistant

The double-underscore MCP scheme is deliberate: it matches the namespacing every MCP client already uses, so imported tools read the same here as everywhere else. Agent and skill IDs are neutral and stable for referencing; the display name is where a product's branding lives.

Published packages

PackageResponsibility
@forge/coreIDs, execution context, events, content parts and errors
@forge/modelsProvider/model registry and capability resolution
@forge/agentsDeclarative, versioned agent manifests
@forge/runtimeAI execution loop and run lifecycle
@forge/toolsProviders, catalog, discovery and dispatch
@forge/mcpOutbound MCP server consumption as authorization-filtered tools
@forge/authorizationPermission model, tool filtering and scope resolution
@forge/contextContext providers, budgeting and prompt assembly
@forge/skillsVersioned lazy-loaded skills
@forge/hitlQuestions, approvals and durable continuation
@forge/persistenceStorage ports, schema provisioning and unit-of-work contract
@forge/usageToken counting, cost accounting, quotas and rollups
@forge/ragIngestion and permission-aware retrieval
@forge/filesAttachments, metadata, versions and processing
@forge/documentsParsing and deterministic document edits
@forge/graphqlSchema and thin resolvers
@forge/reactHeadless client state and subscriptions
@forge/uiOptional reusable components
@forge/evalsDatasets, runners and graders

Composition API

const platform = createAgentPlatform({
modelRegistry,
stores,
jobDispatcher,
realtimePublisher,
lockStore,
authorizationPolicy,
usageRecorder,
evaluationService,
vectorIndex,
blobStore,
});

platform.registerToolProvider(domainToolProvider);
platform.registerContextProvider(domainContextProvider);
platform.registerAgent(assistant);
platform.registerMcpServer(tenantMcpConnection);

The names above are neutral placeholders. A consuming application supplies its own providers and agents; product-specific names (for example ShareFlow's social assistant) live only in that application's integration package.

Acceptance criteria

  • Generic packages build and test without ShareFlow or Twenty installed.
  • A minimal example app can compose memory storage and a provider adapter.
  • ShareFlow-specific functionality exists only under its integration.
  • Package dependency checks fail CI on forbidden imports or circular dependencies.