@plotday/twister 0.26.0 → 0.28.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.
- package/README.md +10 -4
- package/bin/commands/deploy.js +15 -7
- package/bin/commands/deploy.js.map +1 -1
- package/bin/templates/AGENTS.template.md +119 -33
- package/bin/utils/sse.js +19 -1
- package/bin/utils/sse.js.map +1 -1
- package/cli/templates/AGENTS.template.md +119 -33
- package/dist/common/calendar.d.ts +23 -11
- package/dist/common/calendar.d.ts.map +1 -1
- package/dist/common/messaging.d.ts +21 -6
- package/dist/common/messaging.d.ts.map +1 -1
- package/dist/common/projects.d.ts +25 -17
- package/dist/common/projects.d.ts.map +1 -1
- package/dist/docs/assets/hierarchy.js +1 -1
- package/dist/docs/assets/navigation.js +1 -1
- package/dist/docs/assets/search.js +1 -1
- package/dist/docs/classes/tool.Tool.html +20 -15
- package/dist/docs/classes/tools_ai.AI.html +1 -1
- package/dist/docs/classes/tools_callbacks.Callbacks.html +1 -1
- package/dist/docs/classes/tools_integrations.Integrations.html +1 -1
- package/dist/docs/classes/tools_network.Network.html +1 -1
- package/dist/docs/classes/tools_plot.Plot.html +2 -2
- package/dist/docs/classes/tools_store.Store.html +13 -4
- package/dist/docs/classes/tools_tasks.Tasks.html +1 -1
- package/dist/docs/classes/tools_twists.Twists.html +1 -1
- package/dist/docs/documents/Building_Custom_Tools.html +8 -2
- package/dist/docs/documents/Built-in_Tools.html +19 -8
- package/dist/docs/documents/Core_Concepts.html +14 -6
- package/dist/docs/documents/Getting_Started.html +11 -4
- package/dist/docs/documents/Runtime_Environment.html +1 -1
- package/dist/docs/enums/plot.ActivityLinkType.html +5 -5
- package/dist/docs/enums/plot.ActivityType.html +4 -4
- package/dist/docs/enums/plot.ActorType.html +4 -4
- package/dist/docs/enums/plot.ConferencingProvider.html +6 -6
- package/dist/docs/enums/tools_plot.ContactAccess.html +1 -1
- package/dist/docs/functions/index.Uuid.Generate.html +1 -0
- package/dist/docs/hierarchy.html +1 -1
- package/dist/docs/index.html +10 -0
- package/dist/docs/interfaces/common_calendar.CalendarTool.html +23 -13
- package/dist/docs/media/SYNC_STRATEGIES.md +651 -0
- package/dist/docs/modules/index.Uuid.html +1 -0
- package/dist/docs/modules/index.html +1 -1
- package/dist/docs/modules/plot.html +1 -1
- package/dist/docs/types/index.Uuid.html +1 -0
- package/dist/docs/types/plot.Activity.html +17 -15
- package/dist/docs/types/plot.ActivityCommon.html +16 -16
- package/dist/docs/types/plot.ActivityLink.html +1 -1
- package/dist/docs/types/plot.ActivityMeta.html +5 -25
- package/dist/docs/types/plot.ActivityUpdate.html +5 -4
- package/dist/docs/types/plot.ActivityWithNotes.html +1 -1
- package/dist/docs/types/plot.Actor.html +5 -5
- package/dist/docs/types/plot.ActorId.html +1 -1
- package/dist/docs/types/plot.ContentType.html +1 -1
- package/dist/docs/types/plot.NewActivity.html +32 -14
- package/dist/docs/types/plot.NewActivityWithNotes.html +1 -1
- package/dist/docs/types/plot.NewActor.html +3 -0
- package/dist/docs/types/plot.NewContact.html +4 -4
- package/dist/docs/types/plot.NewNote.html +14 -4
- package/dist/docs/types/plot.NewPriority.html +1 -1
- package/dist/docs/types/plot.NewTags.html +2 -0
- package/dist/docs/types/plot.Note.html +6 -3
- package/dist/docs/types/plot.NoteUpdate.html +7 -6
- package/dist/docs/types/plot.PickPriorityConfig.html +2 -2
- package/dist/docs/types/plot.Priority.html +3 -3
- package/dist/docs/types/plot.SyncUpdate.html +15 -0
- package/dist/docs/types/plot.Tags.html +2 -1
- package/dist/llm-docs/common/calendar.d.ts +1 -1
- package/dist/llm-docs/common/calendar.d.ts.map +1 -1
- package/dist/llm-docs/common/calendar.js +1 -1
- package/dist/llm-docs/common/calendar.js.map +1 -1
- package/dist/llm-docs/common/messaging.d.ts +1 -1
- package/dist/llm-docs/common/messaging.d.ts.map +1 -1
- package/dist/llm-docs/common/messaging.js +1 -1
- package/dist/llm-docs/common/messaging.js.map +1 -1
- package/dist/llm-docs/common/projects.d.ts +1 -1
- package/dist/llm-docs/common/projects.d.ts.map +1 -1
- package/dist/llm-docs/common/projects.js +1 -1
- package/dist/llm-docs/common/projects.js.map +1 -1
- package/dist/llm-docs/plot.d.ts +1 -1
- package/dist/llm-docs/plot.d.ts.map +1 -1
- package/dist/llm-docs/plot.js +1 -1
- package/dist/llm-docs/plot.js.map +1 -1
- package/dist/llm-docs/tool.d.ts +1 -1
- package/dist/llm-docs/tool.d.ts.map +1 -1
- package/dist/llm-docs/tool.js +1 -1
- package/dist/llm-docs/tool.js.map +1 -1
- package/dist/llm-docs/tools/plot.d.ts +1 -1
- package/dist/llm-docs/tools/plot.d.ts.map +1 -1
- package/dist/llm-docs/tools/plot.js +1 -1
- package/dist/llm-docs/tools/plot.js.map +1 -1
- package/dist/llm-docs/tools/store.d.ts +1 -1
- package/dist/llm-docs/tools/store.d.ts.map +1 -1
- package/dist/llm-docs/tools/store.js +1 -1
- package/dist/llm-docs/tools/store.js.map +1 -1
- package/dist/llm-docs/twist-guide-template.d.ts +1 -1
- package/dist/llm-docs/twist-guide-template.d.ts.map +1 -1
- package/dist/llm-docs/twist-guide-template.js +1 -1
- package/dist/llm-docs/twist-guide-template.js.map +1 -1
- package/dist/plot.d.ts +230 -149
- package/dist/plot.d.ts.map +1 -1
- package/dist/plot.js +1 -0
- package/dist/plot.js.map +1 -1
- package/dist/tool.d.ts +16 -2
- package/dist/tool.d.ts.map +1 -1
- package/dist/tool.js +16 -2
- package/dist/tool.js.map +1 -1
- package/dist/tools/plot.d.ts +2 -2
- package/dist/tools/plot.d.ts.map +1 -1
- package/dist/tools/plot.js +1 -1
- package/dist/tools/plot.js.map +1 -1
- package/dist/tools/store.d.ts +16 -0
- package/dist/tools/store.d.ts.map +1 -1
- package/dist/tools/store.js.map +1 -1
- package/dist/twist-guide.d.ts +1 -1
- package/dist/twist-guide.d.ts.map +1 -1
- package/dist/utils/uuid.d.ts +7 -0
- package/dist/utils/uuid.d.ts.map +1 -0
- package/dist/utils/uuid.js +9 -0
- package/dist/utils/uuid.js.map +1 -0
- package/package.json +9 -2
|
@@ -27,19 +27,40 @@ Plot Twists are TypeScript classes that extend the `Twist` base class. Twists in
|
|
|
27
27
|
|
|
28
28
|
1. **Always create Activities with an initial Note** - The title is just a summary; detailed content goes in Notes
|
|
29
29
|
2. **Add Notes to existing Activities for updates** - Don't create a new Activity for each related message
|
|
30
|
-
3. **Use
|
|
31
|
-
4. **
|
|
30
|
+
3. **Use Activity.source and Note.key for automatic upserts (Recommended)** - Set Activity.source to the external item's URL for deduplication, and use Note.key for upsertable note content. No manual ID tracking needed.
|
|
31
|
+
4. **For advanced cases, use generated UUIDs** - Only when you need multiple Plot activities per external item (see SYNC_STRATEGIES.md)
|
|
32
|
+
5. **Most Activities should be `ActivityType.Note`** - Use `Action` only for tasks with `done`, use `Event` only for items with `start`/`end`
|
|
32
33
|
|
|
33
|
-
### Decision Tree
|
|
34
|
+
### Recommended Decision Tree (Strategy 2: Upsert via Source/Key)
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
New event/task/conversation from external system?
|
|
38
|
+
├─ Has stable URL or ID?
|
|
39
|
+
│ └─ Yes → Set Activity.source to the canonical URL/ID
|
|
40
|
+
│ Create Activity (Plot handles deduplication automatically)
|
|
41
|
+
│ Use Note.key for different note types:
|
|
42
|
+
│ - "description" for main content
|
|
43
|
+
│ - "metadata" for status/priority/assignee
|
|
44
|
+
│ - "comment-{id}" for individual comments
|
|
45
|
+
│
|
|
46
|
+
└─ No stable identifier OR need multiple Plot activities per external item?
|
|
47
|
+
└─ Use Advanced Pattern (Strategy 3: Generate and Store IDs)
|
|
48
|
+
See SYNC_STRATEGIES.md for details
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Advanced Decision Tree (Strategy 3: Generate and Store IDs)
|
|
52
|
+
|
|
53
|
+
Only use when source/key upserts aren't sufficient (e.g., creating multiple activities from one external item):
|
|
34
54
|
|
|
35
55
|
```
|
|
36
56
|
New event/task/conversation?
|
|
37
|
-
├─ Yes →
|
|
38
|
-
│
|
|
57
|
+
├─ Yes → Generate UUID with Uuid.Generate()
|
|
58
|
+
│ Create new Activity with that UUID
|
|
59
|
+
│ Store mapping: external_id → activity_uuid
|
|
39
60
|
│
|
|
40
|
-
└─ No (update/reply/comment) →
|
|
41
|
-
├─ Found → Add Note to existing Activity
|
|
42
|
-
└─ Not found → Create new Activity with
|
|
61
|
+
└─ No (update/reply/comment) → Look up mapping by external_id
|
|
62
|
+
├─ Found → Add Note to existing Activity using stored UUID
|
|
63
|
+
└─ Not found → Create new Activity with UUID + store mapping
|
|
43
64
|
```
|
|
44
65
|
|
|
45
66
|
## Twist Structure Pattern
|
|
@@ -52,6 +73,7 @@ import {
|
|
|
52
73
|
twist,
|
|
53
74
|
} from "@plotday/twister";
|
|
54
75
|
import { Plot } from "@plotday/twister/tools/plot";
|
|
76
|
+
import { Uuid } from "@plotday/twister/utils/uuid";
|
|
55
77
|
|
|
56
78
|
export default class MyTwist extends Twist<MyTwist> {
|
|
57
79
|
build(build: ToolBuilder) {
|
|
@@ -267,7 +289,9 @@ async onAuthComplete(authResult: { authToken: string }, provider: string) {
|
|
|
267
289
|
|
|
268
290
|
## Sync Pattern
|
|
269
291
|
|
|
270
|
-
|
|
292
|
+
### Recommended: Upsert via Source/Key (Strategy 2)
|
|
293
|
+
|
|
294
|
+
Pattern for syncing external data using automatic upserts - **no manual ID tracking needed**:
|
|
271
295
|
|
|
272
296
|
```typescript
|
|
273
297
|
async startSync(calendarId: string): Promise<void> {
|
|
@@ -282,29 +306,43 @@ async startSync(calendarId: string): Promise<void> {
|
|
|
282
306
|
}
|
|
283
307
|
|
|
284
308
|
async handleEvent(
|
|
285
|
-
|
|
309
|
+
event: ExternalEvent,
|
|
286
310
|
calendarId: string
|
|
287
311
|
): Promise<void> {
|
|
288
|
-
//
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
)
|
|
312
|
+
// Use the event's canonical URL as the source for automatic deduplication
|
|
313
|
+
const activity: NewActivityWithNotes = {
|
|
314
|
+
source: event.htmlLink, // or event.url, depending on your external system
|
|
315
|
+
type: ActivityType.Event,
|
|
316
|
+
title: event.summary || "(No title)",
|
|
317
|
+
start: event.start?.dateTime || event.start?.date || null,
|
|
318
|
+
end: event.end?.dateTime || event.end?.date || null,
|
|
319
|
+
notes: [],
|
|
320
|
+
};
|
|
293
321
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
322
|
+
// Add description as an upsertable note
|
|
323
|
+
if (event.description) {
|
|
324
|
+
activity.notes.push({
|
|
325
|
+
activity: { source: event.htmlLink },
|
|
326
|
+
key: "description", // This key enables upserts - same key updates the note
|
|
327
|
+
content: event.description,
|
|
328
|
+
});
|
|
304
329
|
}
|
|
305
330
|
|
|
306
|
-
//
|
|
307
|
-
|
|
331
|
+
// Add attendees as an upsertable note
|
|
332
|
+
if (event.attendees?.length) {
|
|
333
|
+
const attendeeList = event.attendees
|
|
334
|
+
.map(a => `- ${a.email}${a.displayName ? ` (${a.displayName})` : ''}`)
|
|
335
|
+
.join('\n');
|
|
336
|
+
|
|
337
|
+
activity.notes.push({
|
|
338
|
+
activity: { source: event.htmlLink },
|
|
339
|
+
key: "attendees", // Different key for different note types
|
|
340
|
+
content: `## Attendees\n${attendeeList}`,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Create or update - Plot automatically handles deduplication based on source
|
|
345
|
+
await this.tools.plot.createActivity(activity);
|
|
308
346
|
}
|
|
309
347
|
|
|
310
348
|
async stopSync(calendarId: string): Promise<void> {
|
|
@@ -313,6 +351,50 @@ async stopSync(calendarId: string): Promise<void> {
|
|
|
313
351
|
}
|
|
314
352
|
```
|
|
315
353
|
|
|
354
|
+
### Advanced: Generate and Store IDs (Strategy 3)
|
|
355
|
+
|
|
356
|
+
Only use this pattern when you need to create multiple Plot activities from a single external item, or when the external system doesn't provide stable identifiers. See SYNC_STRATEGIES.md for details.
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
async handleEventAdvanced(
|
|
360
|
+
incomingActivity: NewActivityWithNotes,
|
|
361
|
+
calendarId: string
|
|
362
|
+
): Promise<void> {
|
|
363
|
+
// Extract external event ID from meta (adapt based on your tool's data)
|
|
364
|
+
const externalId = incomingActivity.meta?.eventId;
|
|
365
|
+
|
|
366
|
+
if (!externalId) {
|
|
367
|
+
console.error("Event missing external ID");
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Check if we've already synced this event
|
|
372
|
+
const mappingKey = `event_mapping:${calendarId}:${externalId}`;
|
|
373
|
+
const existingActivityId = await this.get<Uuid>(mappingKey);
|
|
374
|
+
|
|
375
|
+
if (existingActivityId) {
|
|
376
|
+
// Event already exists - add update as a Note (add message to thread)
|
|
377
|
+
if (incomingActivity.notes?.[0]?.content) {
|
|
378
|
+
await this.tools.plot.createNote({
|
|
379
|
+
activity: { id: existingActivityId },
|
|
380
|
+
content: incomingActivity.notes[0].content,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// New event - generate UUID and store mapping
|
|
387
|
+
const activityId = Uuid.Generate();
|
|
388
|
+
await this.set(mappingKey, activityId);
|
|
389
|
+
|
|
390
|
+
// Create new Activity with initial Note (new thread with first message)
|
|
391
|
+
await this.tools.plot.createActivity({
|
|
392
|
+
...incomingActivity,
|
|
393
|
+
id: activityId,
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
316
398
|
## Calendar Selection Pattern
|
|
317
399
|
|
|
318
400
|
Pattern for letting users select from multiple calendars/accounts:
|
|
@@ -405,13 +487,17 @@ async syncBatch(args: any, resourceId: string): Promise<void> {
|
|
|
405
487
|
// Process one batch (keep under time limit)
|
|
406
488
|
const result = await this.fetchBatch(state.nextPageToken);
|
|
407
489
|
|
|
408
|
-
// Process results (
|
|
490
|
+
// Process results using source/key pattern (automatic upserts, no manual tracking)
|
|
409
491
|
for (const item of result.items) {
|
|
410
492
|
await this.tools.plot.createActivity({
|
|
493
|
+
source: item.url, // Use item's canonical URL for automatic deduplication
|
|
411
494
|
type: ActivityType.Note,
|
|
412
495
|
title: item.title,
|
|
413
|
-
|
|
414
|
-
|
|
496
|
+
notes: [{
|
|
497
|
+
activity: { source: item.url },
|
|
498
|
+
key: "description", // Use key for upsertable notes
|
|
499
|
+
content: item.description,
|
|
500
|
+
}],
|
|
415
501
|
});
|
|
416
502
|
}
|
|
417
503
|
|
|
@@ -471,9 +557,9 @@ try {
|
|
|
471
557
|
- **Don't use instance variables for state** - Anything stored in memory is lost after function execution. Always use the Store tool for data that needs to persist.
|
|
472
558
|
- **Processing self-created activities** - Other users may change an Activity created by the twist, resulting in an \`activity\` call. Be sure to check the \`changes === null\` and/or \`activity.author.id !== this.id\` to avoid re-processing.
|
|
473
559
|
- **Always create Activities with Notes** - See "Understanding Activities and Notes" section above for the thread/message pattern and decision tree.
|
|
474
|
-
- **Use correct Activity types** - Most should be `ActivityType.Note`. Only use `Action` for tasks with `
|
|
475
|
-
- **Use
|
|
476
|
-
- **Add Notes to existing Activities** -
|
|
560
|
+
- **Use correct Activity types** - Most should be `ActivityType.Note`. Only use `Action` for tasks with `done`, and `Event` for items with `start`/`end`.
|
|
561
|
+
- **Use Activity.source and Note.key for automatic upserts (Recommended)** - Set Activity.source to the external item's URL for automatic deduplication. Only use UUID generation and storage for advanced cases (see SYNC_STRATEGIES.md).
|
|
562
|
+
- **Add Notes to existing Activities** - For source/key pattern, reference activities by source. For UUID pattern, look up stored mappings before creating new Activities. Think thread replies, not new threads.
|
|
477
563
|
- Tools are declared in the `build` method and accessed via `this.tools.toolName` in twist methods.
|
|
478
564
|
- **Don't forget runtime limits** - Each execution has ~10 seconds. Break long operations into batches with the Tasks tool. Process enough items per batch to be efficient, but few enough to stay under time limits.
|
|
479
565
|
- **Always use Callbacks tool for persistent references** - Direct function references don't survive worker restarts.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ActivityLink,
|
|
1
|
+
import type { ActivityLink, SyncUpdate } from "../index";
|
|
2
2
|
/**
|
|
3
3
|
* Represents successful calendar authorization.
|
|
4
4
|
*
|
|
@@ -54,9 +54,13 @@ export interface SyncOptions {
|
|
|
54
54
|
* 5. Start sync for selected calendars
|
|
55
55
|
* 6. Process incoming events via callbacks
|
|
56
56
|
*
|
|
57
|
+
* **Recommended Data Sync Strategy:**
|
|
58
|
+
* Use Activity.source and Note.key for automatic upserts without manual ID tracking.
|
|
59
|
+
* See SYNC_STRATEGIES.md for detailed patterns and when to use alternative approaches.
|
|
60
|
+
*
|
|
57
61
|
* @example
|
|
58
62
|
* ```typescript
|
|
59
|
-
* // Typical calendar integration flow
|
|
63
|
+
* // Typical calendar integration flow using source/key upserts
|
|
60
64
|
* class MyCalendarTwist extends Twist {
|
|
61
65
|
* private googleCalendar: GoogleCalendar;
|
|
62
66
|
*
|
|
@@ -86,11 +90,13 @@ export interface SyncOptions {
|
|
|
86
90
|
* }
|
|
87
91
|
*
|
|
88
92
|
* async onCalendarEvent(
|
|
89
|
-
*
|
|
93
|
+
* syncUpdate: SyncUpdate,
|
|
90
94
|
* syncMeta: { initialSync: boolean }
|
|
91
95
|
* ) {
|
|
92
|
-
* // Step 4: Process synced events
|
|
93
|
-
*
|
|
96
|
+
* // Step 4: Process synced events using source/key pattern
|
|
97
|
+
* // The sync update will automatically use the event's URL as the source
|
|
98
|
+
* // for deduplication - no manual ID tracking needed
|
|
99
|
+
* await this.plot.createActivity(syncUpdate);
|
|
94
100
|
* }
|
|
95
101
|
* }
|
|
96
102
|
* ```
|
|
@@ -123,19 +129,25 @@ export interface CalendarTool {
|
|
|
123
129
|
* event import and ongoing change notifications. The callback function
|
|
124
130
|
* will be invoked for each synced event.
|
|
125
131
|
*
|
|
126
|
-
*
|
|
127
|
-
* -
|
|
128
|
-
* -
|
|
132
|
+
* **Recommended Implementation** (Strategy 2 - Upsert via Source/Key):
|
|
133
|
+
* - Set Activity.source to the event's canonical URL (e.g., event.htmlLink)
|
|
134
|
+
* - Use Note.key for event details (description, attendees, etc.) to enable upserts
|
|
135
|
+
* - No manual ID tracking needed - Plot handles deduplication automatically
|
|
136
|
+
* - Send NewActivityWithNotes for all events (creates new or updates existing)
|
|
137
|
+
* - Set activity.unread = false for initial sync, true for incremental updates
|
|
138
|
+
*
|
|
139
|
+
* **Alternative** (Strategy 3 - Advanced cases):
|
|
140
|
+
* - Use Uuid.Generate() and store ID mappings when creating multiple activities per event
|
|
141
|
+
* - See SYNC_STRATEGIES.md for when this is appropriate
|
|
129
142
|
*
|
|
130
143
|
* @param authToken - Authorization token for calendar access
|
|
131
144
|
* @param calendarId - ID of the calendar to sync
|
|
132
|
-
* @param callback - Function receiving (
|
|
133
|
-
* The activity.unread field indicates whether this is from initial sync.
|
|
145
|
+
* @param callback - Function receiving (syncUpdate, ...extraArgs) for each synced event
|
|
134
146
|
* @param extraArgs - Additional arguments to pass to the callback (type-checked)
|
|
135
147
|
* @returns Promise that resolves when sync setup is complete
|
|
136
148
|
* @throws When auth token is invalid or calendar doesn't exist
|
|
137
149
|
*/
|
|
138
|
-
startSync<TCallback extends (
|
|
150
|
+
startSync<TCallback extends (syncUpdate: SyncUpdate, ...args: any[]) => any>(authToken: string, calendarId: string, callback: TCallback, ...extraArgs: TCallback extends (syncUpdate: any, ...rest: infer R) => any ? R : []): Promise<void>;
|
|
139
151
|
/**
|
|
140
152
|
* Stops synchronizing events from a specific calendar.
|
|
141
153
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../src/common/calendar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../src/common/calendar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAwB,UAAU,EAAE,MAAM,UAAU,CAAC;AAE/E;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;IACvB,6DAA6D;IAC7D,EAAE,EAAE,MAAM,CAAC;IACX,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,0DAA0D;IAC1D,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,gDAAgD;IAChD,OAAO,CAAC,EAAE,IAAI,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;OAMG;IACH,WAAW,CAAC,SAAS,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACvE,QAAQ,EAAE,SAAS,EACnB,GAAG,SAAS,EAAE,SAAS,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,MAAM,CAAC,KAAK,GAAG,GAChE,CAAC,GACD,EAAE,GACL,OAAO,CAAC,YAAY,CAAC,CAAC;IAEzB;;;;;;;;;;OAUG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAErD;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,SAAS,CACP,SAAS,SAAS,CAChB,UAAU,EAAE,UAAU,EACtB,GAAG,IAAI,EAAE,GAAG,EAAE,KACX,GAAG,EAER,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,SAAS,EACnB,GAAG,SAAS,EAAE,SAAS,SAAS,CAC9B,UAAU,EAAE,GAAG,EACf,GAAG,IAAI,EAAE,MAAM,CAAC,KACb,GAAG,GACJ,CAAC,GACD,EAAE,GACL,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChE"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ActivityLink,
|
|
1
|
+
import type { ActivityLink, SyncUpdate } from "../index";
|
|
2
2
|
/**
|
|
3
3
|
* Represents a successful messaging service authorization.
|
|
4
4
|
*
|
|
@@ -41,6 +41,10 @@ export interface MessageSyncOptions {
|
|
|
41
41
|
*
|
|
42
42
|
* All synced messages/emails are converted to ActivityWithNotes objects.
|
|
43
43
|
* Each email thread or chat conversation becomes an Activity with Notes for each message.
|
|
44
|
+
*
|
|
45
|
+
* **Recommended Data Sync Strategy:**
|
|
46
|
+
* Use Activity.source (thread URL or ID) and Note.key (message ID) for automatic upserts.
|
|
47
|
+
* See SYNC_STRATEGIES.md for detailed patterns.
|
|
44
48
|
*/
|
|
45
49
|
export interface MessagingTool {
|
|
46
50
|
/**
|
|
@@ -61,18 +65,29 @@ export interface MessagingTool {
|
|
|
61
65
|
/**
|
|
62
66
|
* Begins synchronizing messages from a specific channel.
|
|
63
67
|
*
|
|
64
|
-
* Email threads and chat conversations are converted to
|
|
65
|
-
*
|
|
66
|
-
*
|
|
68
|
+
* Email threads and chat conversations are converted to SyncUpdate objects,
|
|
69
|
+
* which can be either new items or updates to existing items.
|
|
70
|
+
*
|
|
71
|
+
* **Recommended Implementation** (Strategy 2 - Upsert via Source/Key):
|
|
72
|
+
* - Set Activity.source to the thread/conversation URL or stable ID (e.g., "slack:{channelId}:{threadTs}")
|
|
73
|
+
* - Use Note.key for individual messages (e.g., "message-{messageId}")
|
|
74
|
+
* - Each message becomes a separate note with unique key for upserts
|
|
75
|
+
* - No manual ID tracking needed - Plot handles deduplication automatically
|
|
76
|
+
* - Send NewActivityWithNotes for all threads (creates new or updates existing)
|
|
77
|
+
* - Set activity.unread = false for initial sync, true for incremental updates
|
|
78
|
+
*
|
|
79
|
+
* **Alternative** (Strategy 3 - Advanced cases):
|
|
80
|
+
* - Use Uuid.Generate() and store ID mappings when creating multiple activities per thread
|
|
81
|
+
* - See SYNC_STRATEGIES.md for when this is appropriate
|
|
67
82
|
*
|
|
68
83
|
* @param authToken - Authorization token for access
|
|
69
84
|
* @param channelId - ID of the channel (e.g., channel, inbox) to sync
|
|
70
|
-
* @param callback - Function receiving (
|
|
85
|
+
* @param callback - Function receiving (syncUpdate, ...extraArgs) for each synced conversation
|
|
71
86
|
* @param options - Optional configuration for limiting the sync scope (e.g., time range)
|
|
72
87
|
* @param extraArgs - Additional arguments to pass to the callback (type-checked)
|
|
73
88
|
* @returns Promise that resolves when sync setup is complete
|
|
74
89
|
*/
|
|
75
|
-
startSync<TCallback extends (
|
|
90
|
+
startSync<TCallback extends (syncUpdate: SyncUpdate, ...args: any[]) => any>(authToken: string, channelId: string, callback: TCallback, options?: MessageSyncOptions, ...extraArgs: TCallback extends (syncUpdate: any, ...rest: infer R) => any ? R : []): Promise<void>;
|
|
76
91
|
/**
|
|
77
92
|
* Stops synchronizing messages from a specific channel.
|
|
78
93
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messaging.d.ts","sourceRoot":"","sources":["../../src/common/messaging.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"messaging.d.ts","sourceRoot":"","sources":["../../src/common/messaging.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAwB,UAAU,EAAE,MAAM,UAAU,CAAC;AAE/E;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,EAAE,EAAE,MAAM,CAAC;IACX,uFAAuF;IACvF,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,4EAA4E;IAC5E,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,oDAAoD;IACpD,OAAO,CAAC,EAAE,IAAI,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;;OAMG;IACH,WAAW,CAAC,SAAS,SAAS,CAAC,IAAI,EAAE,aAAa,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACxE,QAAQ,EAAE,SAAS,EACnB,GAAG,SAAS,EAAE,SAAS,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,MAAM,CAAC,KAAK,GAAG,GAChE,CAAC,GACD,EAAE,GACL,OAAO,CAAC,YAAY,CAAC,CAAC;IAEzB;;;;;OAKG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAE1D;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,SAAS,CACP,SAAS,SAAS,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAEjE,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,SAAS,EACnB,OAAO,CAAC,EAAE,kBAAkB,EAC5B,GAAG,SAAS,EAAE,SAAS,SAAS,CAAC,UAAU,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,MAAM,CAAC,KAAK,GAAG,GACtE,CAAC,GACD,EAAE,GACL,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/D"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ActivityLink, ActivityUpdate,
|
|
1
|
+
import type { ActivityLink, ActivityUpdate, SyncUpdate } from "../index";
|
|
2
2
|
/**
|
|
3
3
|
* Represents a successful project management service authorization.
|
|
4
4
|
*
|
|
@@ -41,6 +41,10 @@ export interface ProjectSyncOptions {
|
|
|
41
41
|
*
|
|
42
42
|
* All synced issues/tasks are converted to ActivityWithNotes objects.
|
|
43
43
|
* Each issue becomes an Activity with Notes for the description and comments.
|
|
44
|
+
*
|
|
45
|
+
* **Recommended Data Sync Strategy:**
|
|
46
|
+
* Use Activity.source (issue URL) and Note.key for automatic upserts.
|
|
47
|
+
* See SYNC_STRATEGIES.md for detailed patterns.
|
|
44
48
|
*/
|
|
45
49
|
export interface ProjectTool {
|
|
46
50
|
/**
|
|
@@ -61,27 +65,31 @@ export interface ProjectTool {
|
|
|
61
65
|
/**
|
|
62
66
|
* Begins synchronizing issues from a specific project.
|
|
63
67
|
*
|
|
64
|
-
* Issues and tasks are converted to
|
|
65
|
-
*
|
|
66
|
-
* (description as first note, followed by comments).
|
|
67
|
-
* The Activity.source should be set for deduplication.
|
|
68
|
+
* Issues and tasks are converted to SyncUpdate objects, which can be either
|
|
69
|
+
* new items or updates to existing items.
|
|
68
70
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
+
* **Recommended Implementation** (Strategy 2 - Upsert via Source/Key):
|
|
72
|
+
* - Set Activity.source to the issue's canonical URL (e.g., Linear issue URL, Jira issue URL)
|
|
73
|
+
* - Use Note.key for issue details:
|
|
74
|
+
* - key: "description" for issue description (upserts on changes)
|
|
75
|
+
* - key: "metadata" for status, priority, assignee, etc.
|
|
76
|
+
* - key: "comment-{commentId}" for individual comments (unique per comment)
|
|
77
|
+
* - No manual ID tracking needed - Plot handles deduplication automatically
|
|
78
|
+
* - Send NewActivityWithNotes for all issues (creates new or updates existing)
|
|
79
|
+
* - Set activity.unread = false for initial sync, true for incremental updates
|
|
71
80
|
*
|
|
72
|
-
*
|
|
73
|
-
* -
|
|
74
|
-
* -
|
|
81
|
+
* **Alternative** (Strategy 3 - Advanced cases):
|
|
82
|
+
* - Use Uuid.Generate() and store ID mappings when creating multiple activities per issue
|
|
83
|
+
* - See SYNC_STRATEGIES.md for when this is appropriate
|
|
75
84
|
*
|
|
76
85
|
* @param authToken - Authorization token for access
|
|
77
86
|
* @param projectId - ID of the project to sync
|
|
78
|
-
* @param callback - Function receiving (
|
|
79
|
-
* The issue.unread field indicates whether this is from initial sync.
|
|
87
|
+
* @param callback - Function receiving (syncUpdate, ...extraArgs) for each synced issue
|
|
80
88
|
* @param options - Optional configuration for limiting the sync scope (e.g., time range)
|
|
81
89
|
* @param extraArgs - Additional arguments to pass to the callback (type-checked)
|
|
82
90
|
* @returns Promise that resolves when sync setup is complete
|
|
83
91
|
*/
|
|
84
|
-
startSync<TCallback extends (
|
|
92
|
+
startSync<TCallback extends (syncUpdate: SyncUpdate, ...args: any[]) => any>(authToken: string, projectId: string, callback: TCallback, options?: ProjectSyncOptions, ...extraArgs: TCallback extends (syncUpdate: any, ...rest: infer R) => any ? R : []): Promise<void>;
|
|
85
93
|
/**
|
|
86
94
|
* Stops synchronizing issues from a specific project.
|
|
87
95
|
*
|
|
@@ -97,10 +105,10 @@ export interface ProjectTool {
|
|
|
97
105
|
* sync activity updates back to the external service.
|
|
98
106
|
*
|
|
99
107
|
* The update object contains only the fields that changed, plus id and source.
|
|
100
|
-
* Uses the combination of start and
|
|
101
|
-
* -
|
|
102
|
-
* -
|
|
103
|
-
* -
|
|
108
|
+
* Uses the combination of start and done to determine workflow state:
|
|
109
|
+
* - done set → Completed/Done state
|
|
110
|
+
* - done null + start set → In Progress/Active state
|
|
111
|
+
* - done null + start null → Backlog/Todo state
|
|
104
112
|
*
|
|
105
113
|
* @param authToken - Authorization token for access
|
|
106
114
|
* @param update - ActivityUpdate with changed fields (includes id and source)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/common/projects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,
|
|
1
|
+
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/common/projects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EAEd,UAAU,EACX,MAAM,UAAU,CAAC;AAElB;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,4DAA4D;IAC5D,EAAE,EAAE,MAAM,CAAC;IACX,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,gFAAgF;IAChF,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,oDAAoD;IACpD,OAAO,CAAC,EAAE,IAAI,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;;OAMG;IACH,WAAW,CAAC,SAAS,SAAS,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACtE,QAAQ,EAAE,SAAS,EACnB,GAAG,SAAS,EAAE,SAAS,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,MAAM,CAAC,KAAK,GAAG,GAChE,CAAC,GACD,EAAE,GACL,OAAO,CAAC,YAAY,CAAC,CAAC;IAEzB;;;;;OAKG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,SAAS,CACP,SAAS,SAAS,CAChB,UAAU,EAAE,UAAU,EACtB,GAAG,IAAI,EAAE,GAAG,EAAE,KACX,GAAG,EAER,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,SAAS,EACnB,OAAO,CAAC,EAAE,kBAAkB,EAC5B,GAAG,SAAS,EAAE,SAAS,SAAS,CAC9B,UAAU,EAAE,GAAG,EACf,GAAG,IAAI,EAAE,MAAM,CAAC,KACb,GAAG,GACJ,CAAC,GACD,EAAE,GACL,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9D;;;;;;;;;;;;;;;OAeG;IACH,WAAW,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvE;;;;;;;;;;OAUG;IACH,eAAe,CAAC,CACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
window.hierarchyData = "
|
|
1
|
+
window.hierarchyData = "eJyNkj1PwzAQhv/LzVfTJq2/toqpC0KCDVXIpIZGcWNkH+pQ5b8jJ4DMZC8e7Ofe506+GwTvKYJ+EZsjQrDvznbU+zGCvoHYpHM0FwsaDs/eO0AY+vEEetNIhK/gQEPnTIw23pH3js0UO9MlofMLaKB4WqWy1XKB0J17dwp2TN4ttlxhqxTuZIN8LZHzLYq1RCEkCsWPE4LYZp1UNVLRx4TQcpUHX/tIsRQdX2nm2IJXSFQu2R/KAtOz/aEcvJNNFnxvnHsz3VAxQPeLsr+iso2vZb4OI9mPYJZdKQr7jGZ5aYWW53//YOnqw1A2jgvIfgrKHvFvvEfnqSz5dJ5YQiviRR7/RD7Ycn5MGJvhCoPi+S6bWLMJlDA2wyXDNH0Dl09cpw=="
|
|
@@ -1 +1 @@
|
|
|
1
|
-
window.navigationData = "eJytm99v2zYQx/+
|
|
1
|
+
window.navigationData = "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"
|