Connectors
Connectors bridge external services to the box filesystem. Each implements the Connector interface with a sync() method that pulls data in (and sometimes pushes data out).
How connectors work
External service → Connector.sync() → Writes/reads card files → Git commit
A connector's sync() method:
- Reads its config from
_config/connectors/<name>.jsonand its credentials from the machine-level secret store (docs/secrets.md). Some box-authored integrations (capture, dropbox, raindrop, kie, omdb, tmdb) still keep their own_config/connectors/<name>.secret.json; no built-in connector does - Pulls new data from the external service
- Creates/updates card files in the box
- Stages and commits changes with structured trailers
- Optionally pushes local changes back to the service (two-way sync)
- Creates job cards for downstream processing (e.g., chat jobs for new messages)
Returns a SyncResult with { success, created, updated, pushed?, jobs?, procedures?, error? }. Procedure requests are run by wakeup orchestration after connector writes finish.
Sync rebuilds a connector-managed card's content wholesale from its template; any agent-added field the template doesn't know about is lost unless it's one of the few fields preserve-agent-fields.ts explicitly carries forward (currently just contains).
Connector inventory
| Connector | File | Card types | Direction | Service-injected | Setup doc |
|---|---|---|---|---|---|
| Telegram | telegram.ts | chat-thread | Two-way | Yes | telegram-setup.md |
| Google Calendar | google-calendar.ts | .ics files | Two-way | Yes | calendar.md |
| Gmail | gmail.ts | email-thread, email-message, email-outbound | Two-way (pull + draft upload) | Yes | gmail-setup.md |
| Google Drive | google-drive.ts | sheet | Two-way | Yes | google-drive.md |
Lifecycle
Connectors are called during bbx wakeup:
wakeup.tsloads connector configs for the box- Creates connector instances (with optional service injection)
- Calls
sync()on each - Reports results
Telegram also has a webhook route (routes/telegram.ts) for real-time message delivery, separate from the polling in sync().
Configuration
Each connector reads its non-credential config from _config/connectors/:
google-calendar.json—{ calendars, syncDaysBack, syncDaysForward }gmail.json— named Gmail query rules with a boundedtrackaction, aprocedureaction, or astageaction (record a pending summary and do nothing else), or the equivalentquery/labelsshorthand for a single rule. Every shape states its action explicitly; a missing action, or a missing file, is an error that stops the sync rather than a silent no-op. The history cursor, budgets, and bounded pending summaries live in gitignored_bookkeeping/connectors/gmail.state.json. A live email-thread card is the sole tracking registry; deleting it untracks the thread without changing Gmail. See gmail-setup.md.
Telegram's credentials ({ botToken, webhookSecret }) are the store's telegram-bot/<box> secret, resolved by connectors/telegram-helpers.ts — see docs/secrets.md.
Transient state (last sync offsets, mappings) goes in _bookkeeping/connectors/<name>.state.json or <name>-state.json.
Service injection
Connectors that call external APIs accept an optional service parameter:
export function createTelegramConnector(boxRoot: string, telegram?: TelegramService): Connector
When no service is provided, connectors create real implementations from config files. When a fake is injected (in tests), all API calls go through the fake.
Inside the connector, a helper pattern provides the fallback:
private getTelegram(botToken: string): TelegramService {
return this.telegramService ?? createTelegramService(botToken);
}
See src/services/CLAUDE.md for the full service layer documentation.
Writing a new connector
- Implement the
Connectorinterface (name,produces,sync()) - Declare and resolve credentials via the machine secret store (
docs/secrets.md) rather than a new<name>.secret.jsonfile - Use transient state for sync cursors/offsets
- Stage and commit all file changes with descriptive messages and trailers
- Create job cards when new items need processing
- Register via
registerConnector()in the connector registry - Add a service interface if the connector calls external APIs (see
src/services/CLAUDE.md)
Shared utilities
chat-utils.ts— Thread file management:ensureThreadFile(),appendMessageToThread(),findUnsentAgentMessages(),stampSentMessage(),safeFilename(),updatePersonEntry()calendar-utils.ts— ICS parsing, event formatting, timespan parsingintake-utils.ts—createOrAppendIntakeJob()for creating reactor inbox-processing jobs (legacy reactor path, distinct from the new intake → triage → handle pipeline indocs/triage.md)transient-state.ts—loadTransientState()/saveTransientState()for non-committed statecalendar-config.ts— Calendar sync configuration management