Skip to content

Startup and project setup

roundtable start first runs the checks that need no network: Bun, .env, the configuration, the plugins, the model login, and the public URL. A failed check stops the start with the message roundtable doctor prints for it. Then the host runs these steps in order:

  1. Replacements and providers. Each plugin’s replaces is applied, so the replaced plugin is dropped and the replacement stands where it stood. Then each provider slot is resolved from the plugin that fills it, or the core’s default.

  2. Database and migrations. The database is opened and every plugin’s migrations run, in plugin order.

  3. Setup. Every plugin’s setup runs, in plugin order.

  4. Linking. Tool tiers, hold rules, the session plan, the channel router, and the events are linked. From here sessions(), conversations, surfaces, turns and dashboard() work.

  5. Preflight. Every plugin’s preflight runs. The Discord plugin composes the slash commands here, so a clash stops the start at this step.

  6. Services. Every service starts, plugin by plugin in plugin order. A plugin’s chat surfaces start before its own services.

  7. HTTP. The listeners open.

  8. Background starts. Every service’s startInBackground runs without holding up the boot, and every plugin hears serviceStarted as each ends.

Nothing reaches Discord or the listener unless steps 1 to 5 succeeded.

Setup order is the built-in plugins first: memory, schedule-store, discord, modules, discord-admin, skills, agent-server, seeds. An addon that is switched off is not in the list. Then come your plugins, in the order of plugins in roundtable.config.ts. The built-in schedules plugin comes last, so a due schedule fires only once everything it can reach is running.

run() applies the host’s environment to the process before anything else: the locale, the time zone, and the assistant’s name. So build a command or tool description in setup or later, never at import time. One host runs per process, because the message catalog and the time zone are process-wide. A second run() while one host is running is refused.

If a step fails, the host stops what it had started, closes the database pool, and rethrows. The process then exits non-zero.

On SIGTERM or SIGINT the bot stops serving new work last:

  1. It keeps serving until the channel queue is empty and no service reports busy() work, for at most an hour. Whatever is left is logged and given up on.

  2. Every plugin hears shutdown(left), while every service is still running.

  3. The HTTP listener closes, so no request reaches a service that has stopped.

  4. Services stop in the reverse of the order they started.

  5. The database pool closes.

The package ships .ts source, so your compiler checks it with your project’s options. The guide gives a full tsconfig.json that was tested against an installed tarball. These are the points that matter:

  • Set moduleResolution to bundler, module to Preserve, and types to ["bun"].
  • Turn on strict, verbatimModuleSyntax, allowImportingTsExtensions, and noEmit.
  • Keep skipLibCheck on. Without it the tested example reports 97 dependency-declaration errors.
  • Keep exactOptionalPropertyTypes and noPropertyAccessFromIndexSignature off. Turning either on produces package-source errors even with skipLibCheck.
  • Install TypeScript and @types/bun as development dependencies, as the generated project does.

Type-only exports need import type when verbatimModuleSyntax is on. Copy the whole file from Consumer TypeScript configuration.

A host pins an exact pi-roundtable version, so a change to the core reaches it only after a release. To try a change first, link your checkout:

Terminal window
# in the pi-roundtable checkout
bun link
# in the host
bun link pi-roundtable

The host now imports the checkout’s source, so edits show at once, and the host’s bun run typecheck and bun test run against them. A linked checkout resolves its own dependencies from its own node_modules, so a host’s overrides do not reach it. When the change is done, release the core, set the host’s pi-roundtable to the new exact version, and run bun install to replace the link with the published package. Then run the host’s checks once more against what was published.

For the full text, see What happens when the bot starts and stops and Developing the core and a host together.