aibroker 0.25.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/daemon/core-handlers.d.ts.map +1 -1
  2. package/dist/daemon/core-handlers.js +89 -0
  3. package/dist/daemon/core-handlers.js.map +1 -1
  4. package/dist/daemon/inbound.d.ts +123 -0
  5. package/dist/daemon/inbound.d.ts.map +1 -0
  6. package/dist/daemon/inbound.js +266 -0
  7. package/dist/daemon/inbound.js.map +1 -0
  8. package/dist/daemon/index.d.ts.map +1 -1
  9. package/dist/daemon/index.js +23 -0
  10. package/dist/daemon/index.js.map +1 -1
  11. package/dist/daemon/todoist-cli.d.ts.map +1 -1
  12. package/dist/daemon/todoist-cli.js +73 -1
  13. package/dist/daemon/todoist-cli.js.map +1 -1
  14. package/dist/daemon/todoist-inbox.d.ts +84 -0
  15. package/dist/daemon/todoist-inbox.d.ts.map +1 -0
  16. package/dist/daemon/todoist-inbox.js +169 -0
  17. package/dist/daemon/todoist-inbox.js.map +1 -0
  18. package/dist/daemon/todoist-mirror.d.ts +97 -0
  19. package/dist/daemon/todoist-mirror.d.ts.map +1 -0
  20. package/dist/daemon/todoist-mirror.js +472 -0
  21. package/dist/daemon/todoist-mirror.js.map +1 -0
  22. package/dist/daemon/todoist-oauth.d.ts +0 -6
  23. package/dist/daemon/todoist-oauth.d.ts.map +1 -1
  24. package/dist/daemon/todoist-oauth.js +34 -1
  25. package/dist/daemon/todoist-oauth.js.map +1 -1
  26. package/dist/daemon/todoist-reply.d.ts +15 -0
  27. package/dist/daemon/todoist-reply.d.ts.map +1 -1
  28. package/dist/daemon/todoist-reply.js +31 -0
  29. package/dist/daemon/todoist-reply.js.map +1 -1
  30. package/dist/daemon/todoist-webhook.d.ts.map +1 -1
  31. package/dist/daemon/todoist-webhook.js +91 -0
  32. package/dist/daemon/todoist-webhook.js.map +1 -1
  33. package/dist/mcp/index.js +23 -1
  34. package/dist/mcp/index.js.map +1 -1
  35. package/package.json +1 -1
@@ -0,0 +1,472 @@
1
+ /**
2
+ * daemon/todoist-mirror.ts — new comments, filed where you will actually see them.
3
+ *
4
+ * Todoist never notifies an account about its own activity, and every comment a
5
+ * session writes is written as the account owner. So a reply lands on a task
6
+ * somewhere in a tree of hundreds and is, from the outside, indistinguishable
7
+ * from nothing having happened. `todoist-inbox.ts` answers that on demand. This
8
+ * answers it without being asked: each new comment becomes a task in a mirror
9
+ * project, grouped into a section named after the project the comment came
10
+ * from, linking straight back to the conversation.
11
+ *
12
+ * The sections are managed, not configured. A project that gets commented on
13
+ * gains a section; a section whose items have all been dealt with is removed
14
+ * again on the next run. That keeps the mirror shaped like what is currently
15
+ * outstanding rather than like a history of everything that ever happened —
16
+ * a list nobody prunes is a list nobody reads.
17
+ *
18
+ * Three things this must never do, each of which it would do naturally:
19
+ *
20
+ * - **Feed itself.** A comment on a mirror task is a comment, and mirroring it
21
+ * would create a task, whose comments would create tasks. Anything parented
22
+ * in the mirror project is skipped, and the mirror project is never an
23
+ * ingress project, so its tasks cannot be dispatched as work either.
24
+ * - **Replay.** The read marker moves only after a successful pass, and every
25
+ * mirrored comment id is remembered, because a marker alone loses a race
26
+ * with a comment posted in the same second it was written.
27
+ * - **Delete a section someone else made.** Only sections this module created
28
+ * are pruned, tracked by id, never by name.
29
+ */
30
+ import { join } from "node:path";
31
+ import { homedir } from "node:os";
32
+ import { loadJson, saveJson } from "../core/json-store.js";
33
+ import { log } from "../core/log.js";
34
+ import { audit } from "./audit.js";
35
+ import { getAccessToken } from "./todoist-oauth.js";
36
+ import { projectTree } from "./todoist-projects.js";
37
+ import { fetchCommentActivity } from "./todoist-inbox.js";
38
+ import { fetchTaskBrief } from "./todoist-reply.js";
39
+ import { AGENT_MARK } from "./todoist-webhook.js";
40
+ const FILE = join(homedir(), ".aibroker", "todoist-mirror.json");
41
+ const API = "https://api.todoist.com/api/v1";
42
+ /**
43
+ * How many comment ids to remember.
44
+ *
45
+ * Only needs to cover the window the marker cannot: comments sharing a second
46
+ * with the marker, and a pass that half-failed. A few hundred is generous.
47
+ */
48
+ const SEEN_LIMIT = 500;
49
+ /** Ceiling on one pass, so a long absence cannot flood the mirror in one go. */
50
+ const MAX_PER_RUN = 40;
51
+ function read() {
52
+ const r = loadJson(FILE);
53
+ if (r.status === "ok" && r.data)
54
+ return r.data;
55
+ if (r.status === "unreadable") {
56
+ // Never rewrite what could not be parsed. An empty store would re-mirror
57
+ // everything and, worse, forget which sections are ours to delete.
58
+ log(`todoist-mirror: ${FILE} is unreadable — mirroring is paused until it is fixed or removed`);
59
+ return { lastSeen: "9999", seen: [], sections: {} };
60
+ }
61
+ return {};
62
+ }
63
+ function write(s) {
64
+ if (s.seen && s.seen.length > SEEN_LIMIT)
65
+ s.seen = s.seen.slice(-SEEN_LIMIT);
66
+ saveJson(FILE, s);
67
+ }
68
+ /** The mirror project, or undefined when mirroring is switched off. */
69
+ export function mirrorProjectId() {
70
+ const id = (process.env.TODOIST_MIRROR_PROJECT ?? "").trim();
71
+ return id || undefined;
72
+ }
73
+ async function auth() {
74
+ const t = await getAccessToken();
75
+ return t ? { authorization: `${t.token_type} ${t.access_token}` } : undefined;
76
+ }
77
+ async function listSections(projectId, fetchImpl) {
78
+ const h = await auth();
79
+ if (!h)
80
+ return [];
81
+ const res = await fetchImpl(`${API}/sections?project_id=${encodeURIComponent(projectId)}`, { headers: h });
82
+ if (!res.ok) {
83
+ log(`todoist-mirror: sections listing failed with ${res.status}`);
84
+ return [];
85
+ }
86
+ const body = JSON.parse(await res.text());
87
+ const list = Array.isArray(body) ? body : (body.results ?? []);
88
+ return list.filter((s) => s && s.id).map((s) => ({ id: String(s.id), name: s.name ?? "" }));
89
+ }
90
+ async function createSection(projectId, name, fetchImpl) {
91
+ const h = await auth();
92
+ if (!h)
93
+ return undefined;
94
+ const res = await fetchImpl(`${API}/sections`, {
95
+ method: "POST",
96
+ headers: { ...h, "content-type": "application/json" },
97
+ body: JSON.stringify({ project_id: projectId, name }),
98
+ });
99
+ if (!res.ok) {
100
+ log(`todoist-mirror: section create failed with ${res.status}`);
101
+ return undefined;
102
+ }
103
+ const o = JSON.parse(await res.text());
104
+ return o.id ? String(o.id) : undefined;
105
+ }
106
+ async function deleteSection(sectionId, fetchImpl) {
107
+ const h = await auth();
108
+ if (!h)
109
+ return false;
110
+ const res = await fetchImpl(`${API}/sections/${encodeURIComponent(sectionId)}`, { method: "DELETE", headers: h });
111
+ return res.ok;
112
+ }
113
+ async function activeTaskCount(projectId, sectionId, fetchImpl) {
114
+ const h = await auth();
115
+ if (!h)
116
+ return 1; // Unknown is not empty — never prune on a failed read.
117
+ const res = await fetchImpl(`${API}/tasks?project_id=${encodeURIComponent(projectId)}&section_id=${encodeURIComponent(sectionId)}`, { headers: h });
118
+ if (!res.ok)
119
+ return 1;
120
+ const body = JSON.parse(await res.text());
121
+ const list = Array.isArray(body) ? body : (body.results ?? []);
122
+ return list.length;
123
+ }
124
+ async function createMirrorTask(projectId, sectionId, content, description, fetchImpl) {
125
+ const h = await auth();
126
+ if (!h)
127
+ return undefined;
128
+ const res = await fetchImpl(`${API}/tasks`, {
129
+ method: "POST",
130
+ headers: { ...h, "content-type": "application/json" },
131
+ body: JSON.stringify({
132
+ content,
133
+ description,
134
+ project_id: projectId,
135
+ section_id: sectionId,
136
+ // Due today so it surfaces in Today, which is where the count that
137
+ // actually gets looked at comes from. A mirror nobody opens is the
138
+ // silence this exists to end.
139
+ due_string: "today",
140
+ }),
141
+ });
142
+ if (!res.ok) {
143
+ log(`todoist-mirror: task create failed with ${res.status}: ${(await res.text()).slice(0, 160)}`);
144
+ return undefined;
145
+ }
146
+ const o = JSON.parse(await res.text());
147
+ return o.id ? String(o.id) : undefined;
148
+ }
149
+ /**
150
+ * Post a comment on a task.
151
+ *
152
+ * Unmarked deliberately when mirroring back: see `mirrorBack`. For mirror
153
+ * entries the text is the agent's own and already carries its mark.
154
+ */
155
+ async function addComment(taskId, content, fetchImpl) {
156
+ const h = await auth();
157
+ if (!h)
158
+ return undefined;
159
+ const res = await fetchImpl(`${API}/comments`, {
160
+ method: "POST",
161
+ headers: { ...h, "content-type": "application/json" },
162
+ body: JSON.stringify({ task_id: taskId, content }),
163
+ });
164
+ if (!res.ok) {
165
+ log(`todoist-mirror: comment create failed with ${res.status}`);
166
+ return undefined;
167
+ }
168
+ const o = JSON.parse(await res.text());
169
+ return o.id ? String(o.id) : undefined;
170
+ }
171
+ /** "Claude 🤖 / Home" — the path, because leaf names repeat across the tree. */
172
+ function pathOf(projectId, tree) {
173
+ const parts = [];
174
+ let cur = tree.get(projectId);
175
+ let depth = 0;
176
+ while (cur && depth++ < 12) {
177
+ parts.unshift(cur.name || cur.id);
178
+ cur = cur.parentId ? tree.get(cur.parentId) : undefined;
179
+ }
180
+ return parts.length ? parts.join(" / ") : projectId;
181
+ }
182
+ /**
183
+ * Only one pass at a time.
184
+ *
185
+ * The daemon runs this on a timer and a session can force it by hand, so two
186
+ * passes overlapping is ordinary, not exotic. Both would list the sections
187
+ * before either created one, and both would then create it — which is how the
188
+ * mirror ended up with two "Claude 🤖 / Home" sections within a minute of being
189
+ * switched on. A second caller joins the pass in flight rather than racing it.
190
+ */
191
+ let inFlight;
192
+ /**
193
+ * Mirror everything commented on since the last pass.
194
+ *
195
+ * Safe to call repeatedly: the marker only advances at the end, the seen-set
196
+ * makes a repeated pass a no-op rather than a duplicate, and concurrent callers
197
+ * share one run.
198
+ */
199
+ export function syncMirror(fetchImpl = fetch) {
200
+ if (inFlight)
201
+ return inFlight;
202
+ inFlight = runSync(fetchImpl).finally(() => { inFlight = undefined; });
203
+ return inFlight;
204
+ }
205
+ async function runSync(fetchImpl) {
206
+ const out = { mirrored: 0, sectionsCreated: 0, sectionsRemoved: 0, skipped: 0 };
207
+ const mirror = mirrorProjectId();
208
+ if (!mirror)
209
+ return out;
210
+ const store = read();
211
+ const seen = new Set(store.seen ?? []);
212
+ const sections = { ...(store.sections ?? {}) };
213
+ const records = await fetchCommentActivity(fetchImpl);
214
+ if (records.length === 0)
215
+ return out;
216
+ // First ever pass: adopt the current position and mirror nothing.
217
+ //
218
+ // Without this, "no marker" means "everything in the activity window is new",
219
+ // and switching the mirror on would file a hundred historical comments as
220
+ // things to deal with today. The mirror is for what arrives from now on; the
221
+ // backlog is what `todoist_inbox` is for.
222
+ if (!store.lastSeen) {
223
+ const newest = records.map((r) => r.event_date).filter(Boolean).sort().pop();
224
+ write({ ...store, lastSeen: newest ?? new Date().toISOString(), seen: [], sections });
225
+ log(`todoist-mirror: initialised at ${newest ?? "now"} — mirroring comments from here on`);
226
+ return out;
227
+ }
228
+ const fresh = records
229
+ .filter((r) => r.event_date && r.parent_item_id)
230
+ .filter((r) => !store.lastSeen || r.event_date > store.lastSeen)
231
+ .filter((r) => r.parent_project_id !== mirror)
232
+ .filter((r) => {
233
+ const id = r.object_id;
234
+ return !id || !seen.has(id);
235
+ })
236
+ .sort((a, b) => a.event_date.localeCompare(b.event_date));
237
+ if (fresh.length === 0) {
238
+ await prune(mirror, sections, out, fetchImpl);
239
+ write({ ...store, sections });
240
+ return out;
241
+ }
242
+ const batch = fresh.slice(0, MAX_PER_RUN);
243
+ out.skipped = fresh.length - batch.length;
244
+ const tree = await projectTree(fetchImpl);
245
+ const existing = await listSections(mirror, fetchImpl);
246
+ const byName = new Map(existing.map((s) => [s.name, s.id]));
247
+ let newest = store.lastSeen;
248
+ for (const r of batch) {
249
+ const sourceProject = r.parent_project_id ?? "";
250
+ const sectionName = sourceProject ? pathOf(sourceProject, tree) : "Unfiled";
251
+ let sectionId = byName.get(sectionName);
252
+ if (!sectionId) {
253
+ sectionId = await createSection(mirror, sectionName, fetchImpl);
254
+ if (sectionId) {
255
+ byName.set(sectionName, sectionId);
256
+ sections[sectionId] = sourceProject;
257
+ out.sectionsCreated++;
258
+ // Record ownership NOW, not at the end of the pass. A pass that dies
259
+ // between creating a section and writing the store leaves a section
260
+ // nothing claims: prune will never touch it, and the next pass adds a
261
+ // second one beside it. That is exactly what happened on 2026-08-03 —
262
+ // six orphans and three duplicate names from two interrupted runs.
263
+ write({ ...read(), sections });
264
+ }
265
+ }
266
+ const brief = await fetchTaskBrief(r.parent_item_id, fetchImpl);
267
+ const comment = (r.extra_data?.content ?? "").replace(/\s+/g, " ").trim();
268
+ const title = brief.title ?? `Task ${r.parent_item_id}`;
269
+ const url = brief.url ?? `https://app.todoist.com/app/task/${r.parent_item_id}`;
270
+ const id = await createMirrorTask(mirror, sectionId, `💬 ${title}`.slice(0, 480), `${comment.slice(0, 800)}\n\n[Open the task](${url})`, fetchImpl);
271
+ if (!id)
272
+ continue;
273
+ out.mirrored++;
274
+ const commentId = r.object_id;
275
+ if (commentId)
276
+ seen.add(commentId);
277
+ if (!newest || r.event_date > newest)
278
+ newest = r.event_date;
279
+ }
280
+ await prune(mirror, sections, out, fetchImpl);
281
+ write({ lastSeen: newest, seen: [...seen], sections });
282
+ audit({
283
+ action: "todoist-mirror", actor: "aibroker", target: `todoist:project:${mirror}`,
284
+ outcome: "synced",
285
+ reason: `${out.mirrored} mirrored, ${out.sectionsCreated} sections added, ${out.sectionsRemoved} removed${out.skipped ? `, ${out.skipped} deferred` : ""}`,
286
+ });
287
+ log(`todoist-mirror: ${out.mirrored} mirrored, +${out.sectionsCreated}/-${out.sectionsRemoved} sections`);
288
+ return out;
289
+ }
290
+ /**
291
+ * Remove our own sections that have nothing left in them.
292
+ *
293
+ * Only ids we recorded on creation are considered, so a section someone made by
294
+ * hand survives regardless of what it is called. A section that cannot be read
295
+ * counts as non-empty — losing a section is worse than keeping an empty one.
296
+ */
297
+ async function prune(mirror, sections, out, fetchImpl) {
298
+ for (const sectionId of Object.keys(sections)) {
299
+ const n = await activeTaskCount(mirror, sectionId, fetchImpl);
300
+ if (n > 0)
301
+ continue;
302
+ if (await deleteSection(sectionId, fetchImpl)) {
303
+ delete sections[sectionId];
304
+ out.sectionsRemoved++;
305
+ }
306
+ }
307
+ }
308
+ // --- the direct path -----------------------------------------------------
309
+ /**
310
+ * Mirror a comment we just wrote, without going back to Todoist to find it.
311
+ *
312
+ * This is the primary path, and polling is not the fallback for it — retrying
313
+ * is. The reasoning: Todoist stays silent only about the account's OWN
314
+ * activity, and the bridge writes as the account. Everyone else's comments it
315
+ * already notifies about. So the set of comments that need mirroring is exactly
316
+ * the set we produced, and we know each one at the moment we write it. Reading
317
+ * the activity log to rediscover our own writes was a round trip to learn what
318
+ * we already knew, and it put a five-minute delay on the one case somebody is
319
+ * actually watching.
320
+ *
321
+ * Failures are queued rather than dropped and retried on the next write, so
322
+ * removing the poll does not reintroduce silent loss.
323
+ */
324
+ export async function mirrorComment(comment, fetchImpl = fetch) {
325
+ const mirror = mirrorProjectId();
326
+ if (!mirror)
327
+ return false;
328
+ // Anything the mirror project itself accumulates is not news. Without this a
329
+ // comment on a mirror entry would mirror itself, forever.
330
+ const brief = await fetchTaskBrief(comment.taskId, fetchImpl);
331
+ const store = read();
332
+ const queued = [...(store.pending ?? [])];
333
+ const ok = await fileOne(mirror, comment, brief, fetchImpl);
334
+ if (!ok) {
335
+ queued.push({ ...comment, at: new Date().toISOString() });
336
+ write({ ...read(), pending: queued.slice(-50) });
337
+ log(`todoist-mirror: could not file ${comment.taskId} — queued for retry`);
338
+ return false;
339
+ }
340
+ // Retry whatever an earlier failure left behind, now that we know Todoist is
341
+ // answering. Anything still failing stays queued.
342
+ const stillPending = [];
343
+ for (const p of queued) {
344
+ const b = await fetchTaskBrief(p.taskId, fetchImpl);
345
+ if (!(await fileOne(mirror, p, b, fetchImpl)))
346
+ stillPending.push(p);
347
+ }
348
+ const after = read();
349
+ const seen = new Set(after.seen ?? []);
350
+ if (comment.commentId)
351
+ seen.add(comment.commentId);
352
+ write({ ...after, seen: [...seen], pending: stillPending });
353
+ return true;
354
+ }
355
+ async function fileOne(mirror, comment, brief, fetchImpl) {
356
+ try {
357
+ const sourceProject = brief.projectId ?? "";
358
+ if (sourceProject === mirror)
359
+ return true; // Nothing to do, and not an error.
360
+ const tree = await projectTree(fetchImpl);
361
+ const sectionName = sourceProject ? pathOf(sourceProject, tree) : "Unfiled";
362
+ const store = read();
363
+ const sections = { ...(store.sections ?? {}) };
364
+ const existing = await listSections(mirror, fetchImpl);
365
+ let sectionId = existing.find((s) => s.name === sectionName)?.id;
366
+ if (!sectionId) {
367
+ sectionId = await createSection(mirror, sectionName, fetchImpl);
368
+ if (!sectionId)
369
+ return false;
370
+ sections[sectionId] = sourceProject;
371
+ write({ ...read(), sections });
372
+ }
373
+ const title = brief.title ?? `Task ${comment.taskId}`;
374
+ const url = brief.url ?? `https://app.todoist.com/app/task/${comment.taskId}`;
375
+ const flat = comment.text.replace(/\s+/g, " ").trim();
376
+ // Description carries an EXCERPT and the link, not the comment.
377
+ //
378
+ // A description is a field with a limit and no formatting affordances; a
379
+ // long reply pasted into one is a wall nobody reads and, past the limit, a
380
+ // reply that silently loses its ending. The full text goes where text
381
+ // belongs — a comment on the mirror task — so the entry stays scannable and
382
+ // nothing is truncated where it matters. The link is the anchor the
383
+ // mirror-back path reads to find the source task, so its format is load
384
+ // bearing, not decorative.
385
+ const excerpt = flat.length > 180 ? `${flat.slice(0, 180)}…` : flat;
386
+ const id = await createMirrorTask(mirror, sectionId, `💬 ${title}`.slice(0, 480), `${excerpt}\n\n[Open the task](${url})`, fetchImpl);
387
+ if (!id)
388
+ return false;
389
+ // Full text as the first comment. Failing here leaves the entry in place
390
+ // with its excerpt and link — degraded, but not lost, and the link still
391
+ // reaches the real conversation.
392
+ if (flat.length > excerpt.length) {
393
+ // MARKED, and that is not cosmetic. `mirrorBack` carries any unmarked
394
+ // comment on a mirror entry to the source task — that is the whole point
395
+ // of it. This comment IS the source task's comment, already; carrying it
396
+ // back would post a verbatim duplicate onto the conversation it came
397
+ // from. Observed on 2026-08-03: the split landed, and the copy bounced
398
+ // straight back as a second identical reply.
399
+ await addComment(id, `${AGENT_MARK} ${comment.text}`, fetchImpl);
400
+ }
401
+ return true;
402
+ }
403
+ catch (e) {
404
+ log(`todoist-mirror: file failed — ${e instanceof Error ? e.message : String(e)}`);
405
+ return false;
406
+ }
407
+ }
408
+ // --- mirror-back ---------------------------------------------------------
409
+ /**
410
+ * The source task a mirror entry points at, read from its description link.
411
+ *
412
+ * Stateless on purpose. A stored map would have to survive restarts, stay in
413
+ * step with tasks the user deletes by hand, and be pruned; the link is already
414
+ * in the entry, is the thing the human clicks, and cannot drift out of sync
415
+ * with the entry it belongs to.
416
+ */
417
+ export function sourceTaskOf(description) {
418
+ const m = /app\.todoist\.com\/app\/task\/([A-Za-z0-9]+)/.exec(description ?? "");
419
+ return m?.[1];
420
+ }
421
+ /**
422
+ * Carry a comment written on a mirror entry back to the real task.
423
+ *
424
+ * The mirror is where the reader's attention is, so replying there instead of
425
+ * on the source task is not a mistake anyone will stop making — it is the
426
+ * obvious thing to do, and Todoist offers no way to deep-link to an individual
427
+ * comment that would make the source easier to reach than the entry in front of
428
+ * you. So rather than train the human, carry the comment.
429
+ *
430
+ * Loop safety rests on one asymmetry: this writes to the SOURCE task, and the
431
+ * direct mirror path is only ever invoked by `todoist_reply`. Nothing re-reads
432
+ * this write, so there is no cycle to break. Agent-marked comments are ignored
433
+ * regardless, so a session answering inside the mirror cannot bounce either.
434
+ */
435
+ export async function mirrorBack(ev, fetchImpl = fetch) {
436
+ const mirror = mirrorProjectId();
437
+ if (!mirror)
438
+ return { carried: false, reason: "mirroring is off" };
439
+ if (ev.projectId && ev.projectId !== mirror)
440
+ return { carried: false, reason: "not a mirror task" };
441
+ // Our own writes are marked; carrying them back would echo a reply into the
442
+ // conversation it came from.
443
+ if (ev.text.trimStart().startsWith(AGENT_MARK))
444
+ return { carried: false, reason: "agent comment, ignored" };
445
+ const brief = await fetchTaskBrief(ev.taskId, fetchImpl);
446
+ if (brief.projectId && brief.projectId !== mirror)
447
+ return { carried: false, reason: "not a mirror task" };
448
+ const source = await sourceFromDescription(ev.taskId, fetchImpl);
449
+ if (!source)
450
+ return { carried: false, reason: "mirror entry has no source link" };
451
+ const id = await addComment(source, ev.text, fetchImpl);
452
+ if (!id)
453
+ return { carried: false, reason: "could not post on the source task" };
454
+ audit({
455
+ action: "todoist-mirror", actor: "aibroker", target: `todoist:task:${source}`,
456
+ outcome: "carried-back", reason: `from mirror entry ${ev.taskId}`,
457
+ });
458
+ log(`todoist-mirror: carried a comment from mirror entry ${ev.taskId} back to ${source}`);
459
+ return { carried: true };
460
+ }
461
+ /** Read a mirror entry's description to find the task it mirrors. */
462
+ async function sourceFromDescription(taskId, fetchImpl) {
463
+ const h = await auth();
464
+ if (!h)
465
+ return undefined;
466
+ const res = await fetchImpl(`${API}/tasks/${encodeURIComponent(taskId)}`, { headers: h });
467
+ if (!res.ok)
468
+ return undefined;
469
+ const t = JSON.parse(await res.text());
470
+ return sourceTaskOf(t.description);
471
+ }
472
+ //# sourceMappingURL=todoist-mirror.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"todoist-mirror.js","sourceRoot":"","sources":["../../src/daemon/todoist-mirror.ts"],"names":[],"mappings":"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"}
@@ -69,12 +69,6 @@ export declare function isExpired(t: StoredToken, now?: number): boolean;
69
69
  * sends whoever debugs it to the wrong place.
70
70
  */
71
71
  export declare function refreshAccessToken(clientId: string, clientSecret: string, token: StoredToken, fetchImpl?: typeof fetch): Promise<StoredToken>;
72
- /**
73
- * The token to use right now, refreshed if it is about to expire.
74
- *
75
- * Every caller should go through this rather than `loadToken()`: an hour is
76
- * short enough that "it worked when I tested it" proves nothing.
77
- */
78
72
  export declare function getAccessToken(fetchImpl?: typeof fetch): Promise<StoredToken | null>;
79
73
  export interface OAuthDeps {
80
74
  clientId?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"todoist-oauth.d.ts","sourceRoot":"","sources":["../../src/daemon/todoist-oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAaH,iFAAiF;AACjF,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA4BD,+DAA+D;AAC/D,wBAAgB,SAAS,IAAI,MAAM,CAIlC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAU7F;AAED,wBAAgB,SAAS,IAAI,WAAW,GAAG,IAAI,CAE9C;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,WAAW,GAAG,IAAI,CAE9C;AAED,iEAAiE;AACjE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnF;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,WAAW,CAAC,CAUtB;AAwCD,wBAAgB,SAAS,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,GAAE,MAAmB,GAAG,OAAO,CAG3E;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,WAAW,EAClB,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,WAAW,CAAC,CA4BtB;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,SAAS,GAAE,OAAO,KAAa,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAsBjG;AAgBD,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,SAAS,GACd,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAoD3C"}
1
+ {"version":3,"file":"todoist-oauth.d.ts","sourceRoot":"","sources":["../../src/daemon/todoist-oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAaH,iFAAiF;AACjF,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA4BD,+DAA+D;AAC/D,wBAAgB,SAAS,IAAI,MAAM,CAIlC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAU7F;AAED,wBAAgB,SAAS,IAAI,WAAW,GAAG,IAAI,CAE9C;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,WAAW,GAAG,IAAI,CAE9C;AAED,iEAAiE;AACjE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnF;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,WAAW,CAAC,CAUtB;AAwCD,wBAAgB,SAAS,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,GAAE,MAAmB,GAAG,OAAO,CAG3E;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,WAAW,EAClB,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,WAAW,CAAC,CA4BtB;AA+BD,wBAAsB,cAAc,CAAC,SAAS,GAAE,OAAO,KAAa,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAgCjG;AAgBD,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,SAAS,GACd,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAoD3C"}
@@ -186,6 +186,28 @@ export async function refreshAccessToken(clientId, clientSecret, token, fetchImp
186
186
  * Every caller should go through this rather than `loadToken()`: an hour is
187
187
  * short enough that "it worked when I tested it" proves nothing.
188
188
  */
189
+ /**
190
+ * The refresh in flight, if any.
191
+ *
192
+ * Todoist ROTATES refresh tokens and treats a second use of one as an attack:
193
+ * presenting a spent refresh token returns `invalid_grant — refresh token reuse
194
+ * detected` and REVOKES THE ENTIRE GRANT, not just that token. Re-authorising is
195
+ * then the only way back, and it needs a human at a browser.
196
+ *
197
+ * Which makes concurrent refresh a loaded gun. Several callers — the webhook
198
+ * dispatcher, the comment mirror's timer, a session replying — routinely ask for
199
+ * a token in the same moment. If the token has just expired they each see it
200
+ * expired, each fire a refresh with the same stored refresh token, the first
201
+ * rotates it, and every straggler presents a spent one. The grant dies.
202
+ *
203
+ * That is not hypothetical: it happened on 2026-08-03 at 13:50, minutes after
204
+ * the five-minute mirror timer was added and while the daemon was being
205
+ * restarted repeatedly. Everything Todoist stopped working and the only symptom
206
+ * was 401s.
207
+ *
208
+ * So: one refresh at a time, and everyone else waits for its result.
209
+ */
210
+ let refreshing;
189
211
  export async function getAccessToken(fetchImpl = fetch) {
190
212
  const t = loadToken();
191
213
  if (!t)
@@ -199,7 +221,18 @@ export async function getAccessToken(fetchImpl = fetch) {
199
221
  return t;
200
222
  }
201
223
  try {
202
- return await refreshAccessToken(clientId, clientSecret, t, fetchImpl);
224
+ if (!refreshing) {
225
+ // Re-read inside the guard: a refresh that completed while we were
226
+ // deciding has already written a fresh token to disk, and refreshing the
227
+ // one we loaded a moment ago would spend a token that is already spent.
228
+ refreshing = (async () => {
229
+ const current = loadToken() ?? t;
230
+ if (!isExpired(current))
231
+ return current;
232
+ return refreshAccessToken(clientId, clientSecret, current, fetchImpl);
233
+ })().finally(() => { refreshing = undefined; });
234
+ }
235
+ return await refreshing;
203
236
  }
204
237
  catch (e) {
205
238
  const reason = e instanceof Error ? e.message : String(e);
@@ -1 +1 @@
1
- {"version":3,"file":"todoist-oauth.js","sourceRoot":"","sources":["../../src/daemon/todoist-oauth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"todoist-oauth.js","sourceRoot":"","sources":["../../src/daemon/todoist-oauth.ts"],"names":[],"mappings":"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"}
@@ -87,4 +87,19 @@ export declare function createTask(content: string, opts?: {
87
87
  export declare function setTaskLabel(taskId: string, label: string, present: boolean, fetchImpl?: typeof fetch, attempt?: number): Promise<string[]>;
88
88
  /** The task's current due date, or undefined when it has none or cannot be read. */
89
89
  export declare function fetchTaskDue(taskId: string, fetchImpl?: typeof fetch): Promise<string | undefined>;
90
+ /**
91
+ * The task's title and web link, for a notification that identifies itself.
92
+ *
93
+ * A push saying "a reply was posted" is only marginally better than silence
94
+ * when the tree holds hundreds of tasks: knowing something happened without
95
+ * knowing where still means hunting. The url is what Todoist's own clients
96
+ * open, so the notification is one tap from the conversation it announces.
97
+ *
98
+ * Never throws — a notification that cannot name its task still beats none.
99
+ */
100
+ export declare function fetchTaskBrief(taskId: string, fetchImpl?: typeof fetch): Promise<{
101
+ title?: string;
102
+ url?: string;
103
+ projectId?: string;
104
+ }>;
90
105
  //# sourceMappingURL=todoist-reply.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"todoist-reply.d.ts","sourceRoot":"","sources":["../../src/daemon/todoist-reply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,6EAA6E;AAC7E,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,UAAU,CAAC,CAqBrB;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,WAAW,CAAC,CAiCtB;AAQD;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,EAC3E,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAiC7B;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,EAChB,SAAS,GAAE,OAAO,KAAa,EAC/B,OAAO,SAAI,GACV,OAAO,CAAC,MAAM,EAAE,CAAC,CA2DnB;AAED,oFAAoF;AACpF,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAe7B"}
1
+ {"version":3,"file":"todoist-reply.d.ts","sourceRoot":"","sources":["../../src/daemon/todoist-reply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,6EAA6E;AAC7E,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,UAAU,CAAC,CAqBrB;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,WAAW,CAAC,CAiCtB;AAQD;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,EAC3E,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAiC7B;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,EAChB,SAAS,GAAE,OAAO,KAAa,EAC/B,OAAO,SAAI,GACV,OAAO,CAAC,MAAM,EAAE,CAAC,CA2DnB;AAED,oFAAoF;AACpF,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAe7B;AAED;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAiB/D"}