/** * daemon/todoist-mirror.ts — new comments, filed where you will actually see them. * * Todoist never notifies an account about its own activity, and every comment a * session writes is written as the account owner. So a reply lands on a task * somewhere in a tree of hundreds and is, from the outside, indistinguishable * from nothing having happened. `todoist-inbox.ts` answers that on demand. This * answers it without being asked: each new comment becomes a task in a mirror * project, grouped into a section named after the project the comment came * from, linking straight back to the conversation. * * The sections are managed, not configured. A project that gets commented on * gains a section; a section whose items have all been dealt with is removed * again on the next run. That keeps the mirror shaped like what is currently * outstanding rather than like a history of everything that ever happened — * a list nobody prunes is a list nobody reads. * * Three things this must never do, each of which it would do naturally: * * - **Feed itself.** A comment on a mirror task is a comment, and mirroring it * would create a task, whose comments would create tasks. Anything parented * in the mirror project is skipped, and the mirror project is never an * ingress project, so its tasks cannot be dispatched as work either. * - **Replay.** The read marker moves only after a successful pass, and every * mirrored comment id is remembered, because a marker alone loses a race * with a comment posted in the same second it was written. * - **Delete a section someone else made.** Only sections this module created * are pruned, tracked by id, never by name. */ /** The mirror project, or undefined when mirroring is switched off. */ export declare function mirrorProjectId(): string | undefined; export interface MirrorResult { mirrored: number; sectionsCreated: number; sectionsRemoved: number; skipped: number; } /** * Mirror everything commented on since the last pass. * * Safe to call repeatedly: the marker only advances at the end, the seen-set * makes a repeated pass a no-op rather than a duplicate, and concurrent callers * share one run. */ export declare function syncMirror(fetchImpl?: typeof fetch): Promise; /** * Mirror a comment we just wrote, without going back to Todoist to find it. * * This is the primary path, and polling is not the fallback for it — retrying * is. The reasoning: Todoist stays silent only about the account's OWN * activity, and the bridge writes as the account. Everyone else's comments it * already notifies about. So the set of comments that need mirroring is exactly * the set we produced, and we know each one at the moment we write it. Reading * the activity log to rediscover our own writes was a round trip to learn what * we already knew, and it put a five-minute delay on the one case somebody is * actually watching. * * Failures are queued rather than dropped and retried on the next write, so * removing the poll does not reintroduce silent loss. */ export declare function mirrorComment(comment: { taskId: string; commentId?: string; text: string; }, fetchImpl?: typeof fetch): Promise; /** * The source task a mirror entry points at, read from its description link. * * Stateless on purpose. A stored map would have to survive restarts, stay in * step with tasks the user deletes by hand, and be pruned; the link is already * in the entry, is the thing the human clicks, and cannot drift out of sync * with the entry it belongs to. */ export declare function sourceTaskOf(description: string | undefined): string | undefined; /** * Carry a comment written on a mirror entry back to the real task. * * The mirror is where the reader's attention is, so replying there instead of * on the source task is not a mistake anyone will stop making — it is the * obvious thing to do, and Todoist offers no way to deep-link to an individual * comment that would make the source easier to reach than the entry in front of * you. So rather than train the human, carry the comment. * * Loop safety rests on one asymmetry: this writes to the SOURCE task, and the * direct mirror path is only ever invoked by `todoist_reply`. Nothing re-reads * this write, so there is no cycle to break. Agent-marked comments are ignored * regardless, so a session answering inside the mirror cannot bounce either. */ export declare function mirrorBack(ev: { taskId: string; text: string; projectId?: string; }, fetchImpl?: typeof fetch): Promise<{ carried: boolean; reason?: string; }>; //# sourceMappingURL=todoist-mirror.d.ts.map