/** * PollingScheduler — drives periodic collection from all registered connectors. * Persists lastPollTime to basePath/poll-state.json for crash recovery. */ import type { ConnectorRegistry } from './connector-registry.js'; import type { ChannelConfig, NormalizedItem } from './types.js'; import type { RawIndexSink, RawStore } from './raw-store.js'; import type { ClassifiedItems } from '../../memory/history-extractor.js'; type BatchExtractCallback = (classified: ClassifiedItems) => void | Promise; /** * The canonical key for a channel: the key the connector config declares it under. * * Connectors emit `item.channel` in whatever shape their upstream hands them, and measured * across the live index that is usually a DISPLAY NAME - Trello board names against a * config keyed by 24-character board ids, Slack channel names against a config keyed by * channel ids, and so on for six of seven connectors. `findChannelConfig` already absorbs * that by falling back to a name match, which is why nothing ever appeared broken: the * binding succeeded, and then the un-canonicalised name was written to the event index, * where every downstream reader compared it against the config KEY and matched nothing. * Zero of 30,671 rows were readable in production. * * So the accommodation moves to one place and produces one answer. Names are for people; * identity is the upstream's stable id, and this is where a name becomes one. * * Returns null when the channel is not configured at all - the honest answer, and the one * that keeps an unconfigured channel out of the index rather than inventing a key for it. */ export declare function canonicalChannelKey(item: Pick, channelConfigs: Record>): string | null; /** * `bindConfiguredScope` was here, and it never bound anything. * * It required `project_entity_id` on a channel config and produced a `project` scope. Zero * of the 39 configured channels on the live install declare that field, so it returned * every item unchanged for its whole life. What made this invisible was a one-off backfill * that had written `channel` scopes into the index: April is 100% scoped, the backfill * stopped on 2026-05-20, and from 05-21 onward every single event is unscoped. The live * write path had never bound a scope at all - the data only looked otherwise. * * Raw visibility is decided by the (connector, channel) grant now, which reads the key the * row already carries instead of a parallel namespace that had to be populated. */ export declare class PollingScheduler { private readonly rawStore; private readonly rawIndexSink?; private lastPollTimes; private timers; private readonly stateFile; private isBatchRunning; /** Initial lookback for connectors with no saved state (default: 24h). Set to 0 for all history. */ initialLookbackMs: number; constructor(rawStore: RawStore, basePath: string, options?: { initialLookbackMs?: number; rawIndexSink?: RawIndexSink; }); private restoreState; persistState(): void; pollAll(registry: ConnectorRegistry, channelConfigs: Record>, onBatchExtract: BatchExtractCallback): Promise; startBatch(registry: ConnectorRegistry, channelConfigs: Record>, intervalMinutes: number, onBatchExtract: BatchExtractCallback): void; getLastPollTime(name: string): Date | undefined; /** Reset poll cursor for a connector to re-ingest from a given date. */ resetPollState(name: string, since?: Date): void; stop(): void; } export {}; //# sourceMappingURL=polling-scheduler.d.ts.map