packages/.
Each package has a focused responsibility and exports a public API through its
package entry point. Cross-package consumers use those public exports rather
than another package’s internal source paths.
All packages use ES modules ("type": "module"), strict TypeScript with composite: true project references, and ES2023 as the compilation target.
Package Overview
Package Roles Explained
@comis/shared is the foundation. It defines the Result<T, E> discriminated union and its utility functions. Every other package depends on shared, but shared depends on nothing. This makes it safe to import anywhere without creating circular dependencies.
@comis/core is the heart of the hexagonal architecture. It defines port
interfaces in src/ports/, Zod-validated domain types, the TypedEventBus,
security primitives, configuration schemas, and plugin infrastructure. The
bootstrap() function returns the core AppContainer.
@comis/agent is the execution engine. It contains the PiExecutor that
orchestrates LLM calls, tool execution, and response generation. It also manages
sessions, budgets, circuit breakers, RAG, and context preparation.
@comis/channels contains the 11 platform adapters. Each adapter lives in its own directory (e.g., src/telegram/, src/discord/) with a standard file set: adapter, message mapper, media handler, credential validator, media resolver, voice sender, and plugin wrapper.
@comis/daemon is the runtime composition root. It wires the agent,
channels, gateway, persistence, skills, scheduler, orchestration, logging, and
observability packages; it also manages startup, shutdown, and signals.
@comis/web is a standalone single-page application built with Lit web components, Vite for bundling, and Tailwind CSS for styling. It communicates with the daemon exclusively through the gateway’s HTTP and WebSocket APIs.
The published umbrella package is named
comisai on npm (the directory is packages/comis/). It bundles all @comis/* workspace packages and exposes a comis CLI binary. Subpath imports such as comisai/core or comisai/agent are exposed via the umbrella’s exports map.Dependency relationships
Package manifests and TypeScript project references are the authoritative dependency graph. At a high level:sharedhas no Comis runtime dependency;coredepends onshared.gateway,channels,scheduler, andobservabilitybuild oncoreandshared.memoryandinfraalso use the observability substrate.agentusescore,observability, andscheduler;orchestratorcomposesagent,channels, andcore.skillsuses the agent and observability contracts needed by its tools and integrations.daemonis the runtime composition root.cliuses the daemon client surface plus the core, memory, and observability APIs.observability-otelis the opt-in telemetry extension;webis the standalone SPA;comisaiis the umbrella package that re-exports the workspace packages.
Package Boundary Rules
These rules are mandatory across the codebase and enforced through code review and architectural convention (see AGENTS.md section 6.2).No Cross-Package Internal Imports
Import from the package’s public API, never from internal source paths. Each package exports its public surface throughdist/index.js. This ensures that internal refactoring within a package never breaks consumers.
Keep contracts inward
Core owns shared contracts and ports; runtime packages provide implementations. Cross-package imports must use declared dependencies and public exports. Cross-package lifecycle signals useTypedEventBus where an event is the
appropriate contract.
Inject Logger via Deps Interface
Never import@comis/infra directly in non-infra packages. Pass a logger through the dependency injection pattern so that packages remain decoupled from the logging implementation. This also makes unit testing easier since you can inject a mock logger that captures log calls for assertions.
ES Modules Only
All packages use"type": "module" with .js extensions in import paths. There are no CommonJS modules in the codebase. Import statements use the .js extension even though the source files are .ts:
Build System
TypeScript project references (composite: true) enable incremental builds across the monorepo. Each package has its own tsconfig.json that references its dependencies, so pnpm build compiles all packages in the correct order respecting the reference graph.
To build and test a single package:
Integration tests in
test/integration/ require pnpm build first because they import from dist/, not from source. Run them with pnpm test:integration.Explore the repository
Each package has a focused responsibility and a public entry point. The architecture page explains how packages connect through ports and adapters, while the event bus documents cross-package lifecycle signals.Related
Architecture
How packages connect through ports and adapters
Event Bus
Cross-package communication via typed events
Contributing
Setup and build instructions
Developer Guide
Back to developer guide overview
