@plotday/twister 0.27.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.
Files changed (94) hide show
  1. package/README.md +10 -4
  2. package/bin/templates/AGENTS.template.md +91 -29
  3. package/cli/templates/AGENTS.template.md +91 -29
  4. package/dist/common/calendar.d.ts +18 -21
  5. package/dist/common/calendar.d.ts.map +1 -1
  6. package/dist/common/messaging.d.ts +14 -5
  7. package/dist/common/messaging.d.ts.map +1 -1
  8. package/dist/common/projects.d.ts +20 -10
  9. package/dist/common/projects.d.ts.map +1 -1
  10. package/dist/docs/assets/hierarchy.js +1 -1
  11. package/dist/docs/assets/search.js +1 -1
  12. package/dist/docs/classes/tool.Tool.html +20 -15
  13. package/dist/docs/classes/tools_ai.AI.html +1 -1
  14. package/dist/docs/classes/tools_callbacks.Callbacks.html +1 -1
  15. package/dist/docs/classes/tools_integrations.Integrations.html +1 -1
  16. package/dist/docs/classes/tools_network.Network.html +1 -1
  17. package/dist/docs/classes/tools_plot.Plot.html +2 -2
  18. package/dist/docs/classes/tools_store.Store.html +13 -4
  19. package/dist/docs/classes/tools_tasks.Tasks.html +1 -1
  20. package/dist/docs/classes/tools_twists.Twists.html +1 -1
  21. package/dist/docs/documents/Building_Custom_Tools.html +8 -2
  22. package/dist/docs/documents/Built-in_Tools.html +19 -8
  23. package/dist/docs/documents/Core_Concepts.html +14 -6
  24. package/dist/docs/documents/Getting_Started.html +11 -4
  25. package/dist/docs/enums/plot.ActorType.html +4 -4
  26. package/dist/docs/enums/tools_plot.ContactAccess.html +1 -1
  27. package/dist/docs/hierarchy.html +1 -1
  28. package/dist/docs/index.html +10 -0
  29. package/dist/docs/interfaces/common_calendar.CalendarTool.html +19 -11
  30. package/dist/docs/media/SYNC_STRATEGIES.md +651 -0
  31. package/dist/docs/types/plot.Activity.html +16 -7
  32. package/dist/docs/types/plot.ActivityCommon.html +4 -4
  33. package/dist/docs/types/plot.ActivityUpdate.html +4 -2
  34. package/dist/docs/types/plot.ActivityWithNotes.html +1 -1
  35. package/dist/docs/types/plot.Actor.html +5 -5
  36. package/dist/docs/types/plot.ContentType.html +1 -1
  37. package/dist/docs/types/plot.NewActivity.html +26 -12
  38. package/dist/docs/types/plot.NewActivityWithNotes.html +1 -1
  39. package/dist/docs/types/plot.NewActor.html +1 -1
  40. package/dist/docs/types/plot.NewContact.html +4 -4
  41. package/dist/docs/types/plot.NewNote.html +9 -4
  42. package/dist/docs/types/plot.Note.html +6 -3
  43. package/dist/docs/types/plot.NoteUpdate.html +4 -3
  44. package/dist/docs/types/plot.PickPriorityConfig.html +2 -2
  45. package/dist/docs/types/plot.SyncUpdate.html +1 -1
  46. package/dist/llm-docs/common/calendar.d.ts +1 -1
  47. package/dist/llm-docs/common/calendar.d.ts.map +1 -1
  48. package/dist/llm-docs/common/calendar.js +1 -1
  49. package/dist/llm-docs/common/calendar.js.map +1 -1
  50. package/dist/llm-docs/common/messaging.d.ts +1 -1
  51. package/dist/llm-docs/common/messaging.d.ts.map +1 -1
  52. package/dist/llm-docs/common/messaging.js +1 -1
  53. package/dist/llm-docs/common/messaging.js.map +1 -1
  54. package/dist/llm-docs/common/projects.d.ts +1 -1
  55. package/dist/llm-docs/common/projects.d.ts.map +1 -1
  56. package/dist/llm-docs/common/projects.js +1 -1
  57. package/dist/llm-docs/common/projects.js.map +1 -1
  58. package/dist/llm-docs/plot.d.ts +1 -1
  59. package/dist/llm-docs/plot.d.ts.map +1 -1
  60. package/dist/llm-docs/plot.js +1 -1
  61. package/dist/llm-docs/plot.js.map +1 -1
  62. package/dist/llm-docs/tool.d.ts +1 -1
  63. package/dist/llm-docs/tool.d.ts.map +1 -1
  64. package/dist/llm-docs/tool.js +1 -1
  65. package/dist/llm-docs/tool.js.map +1 -1
  66. package/dist/llm-docs/tools/plot.d.ts +1 -1
  67. package/dist/llm-docs/tools/plot.d.ts.map +1 -1
  68. package/dist/llm-docs/tools/plot.js +1 -1
  69. package/dist/llm-docs/tools/plot.js.map +1 -1
  70. package/dist/llm-docs/tools/store.d.ts +1 -1
  71. package/dist/llm-docs/tools/store.d.ts.map +1 -1
  72. package/dist/llm-docs/tools/store.js +1 -1
  73. package/dist/llm-docs/tools/store.js.map +1 -1
  74. package/dist/llm-docs/twist-guide-template.d.ts +1 -1
  75. package/dist/llm-docs/twist-guide-template.d.ts.map +1 -1
  76. package/dist/llm-docs/twist-guide-template.js +1 -1
  77. package/dist/llm-docs/twist-guide-template.js.map +1 -1
  78. package/dist/plot.d.ts +109 -44
  79. package/dist/plot.d.ts.map +1 -1
  80. package/dist/plot.js.map +1 -1
  81. package/dist/tool.d.ts +16 -2
  82. package/dist/tool.d.ts.map +1 -1
  83. package/dist/tool.js +16 -2
  84. package/dist/tool.js.map +1 -1
  85. package/dist/tools/plot.d.ts +2 -2
  86. package/dist/tools/plot.d.ts.map +1 -1
  87. package/dist/tools/plot.js +1 -1
  88. package/dist/tools/plot.js.map +1 -1
  89. package/dist/tools/store.d.ts +16 -0
  90. package/dist/tools/store.d.ts.map +1 -1
  91. package/dist/tools/store.js.map +1 -1
  92. package/dist/twist-guide.d.ts +1 -1
  93. package/dist/twist-guide.d.ts.map +1 -1
  94. package/package.json +1 -1
package/README.md CHANGED
@@ -136,17 +136,21 @@ Twist tools provide capabilities to twists. They are usually unopinionated and d
136
136
 
137
137
  Think of an **Activity as a thread** and **Notes as messages in that thread**. Always create activities with an initial note, and add notes for updates rather than creating new activities.
138
138
 
139
+ **Data sync:** When syncing from external systems, use `Activity.source` (canonical URL) and `Note.key` for automatic upserts without manual ID tracking. See the [Sync Strategies guide](https://twist.plot.day/documents/Sync_Strategies.html) for detailed patterns.
140
+
139
141
  **Action scheduling:** When creating Actions (tasks), omitting the `start` field defaults to "Do Now" (current time). For most integrations, explicitly set `start: null` to create backlog items ("Do Someday"), only using "Do Now" for urgent or in-progress tasks.
140
142
 
141
143
  ```typescript
142
- // Create an activity with an initial note (thread with first message)
144
+ // Create an activity with source for automatic deduplication
143
145
  await this.tools.plot.createActivity({
146
+ source: "https://github.com/org/repo/pull/123", // Enables automatic upserts
144
147
  type: ActivityType.Action,
145
148
  title: "Review pull request",
146
149
  start: null, // "Do Someday" - backlog item (recommended default)
147
- // Tracked via UUID mapping
148
150
  notes: [
149
151
  {
152
+ activity: { source: "https://github.com/org/repo/pull/123" },
153
+ key: "description", // Use key for upsertable notes
150
154
  content: "New PR ready for review",
151
155
  links: [
152
156
  {
@@ -159,9 +163,10 @@ await this.tools.plot.createActivity({
159
163
  ],
160
164
  });
161
165
 
162
- // Add a note to existing activity (add message to thread)
166
+ // Add or update a note using key (upserts if key exists)
163
167
  await this.tools.plot.createNote({
164
- activity: { id: activityId },
168
+ activity: { source: "https://github.com/org/repo/pull/123" },
169
+ key: "approval", // Using key enables upserts
165
170
  content: "LGTM! Approved ✅",
166
171
  });
167
172
  ```
@@ -199,6 +204,7 @@ plot priority create # Create new priority
199
204
 
200
205
  - [Getting Started](https://twist.plot.day/documents/Getting_Started.html) - Complete walkthrough
201
206
  - [Core Concepts](https://twist.plot.day/documents/Core_Concepts.html) - Twists, tools, and architecture
207
+ - [Sync Strategies](https://twist.plot.day/documents/Sync_Strategies.html) - Data synchronization patterns (upserts, deduplication, ID management)
202
208
  - [Built-in Tools](https://twist.plot.day/documents/Built-in_Tools.html) - Plot, Store, AI, and more
203
209
  - [Building Custom Tools](https://twist.plot.day/documents/Building_Custom_Tools.html) - Create reusable twist tools
204
210
  - [Runtime Environment](https://twist.plot.day/documents/Runtime_Environment.html) - Execution constraints and optimization
@@ -27,10 +27,30 @@ 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. **Track external items using generated UUIDs** - Store mappings between external IDs and Plot UUIDs for deduplication
31
- 4. **Most Activities should be `ActivityType.Note`** - Use `Action` only for tasks with `doneAt`, use `Event` only for items with `start`/`end`
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?
@@ -269,7 +289,9 @@ async onAuthComplete(authResult: { authToken: string }, provider: string) {
269
289
 
270
290
  ## Sync Pattern
271
291
 
272
- Pattern for syncing external data - demonstrates adding Notes to existing Activities:
292
+ ### Recommended: Upsert via Source/Key (Strategy 2)
293
+
294
+ Pattern for syncing external data using automatic upserts - **no manual ID tracking needed**:
273
295
 
274
296
  ```typescript
275
297
  async startSync(calendarId: string): Promise<void> {
@@ -284,6 +306,57 @@ async startSync(calendarId: string): Promise<void> {
284
306
  }
285
307
 
286
308
  async handleEvent(
309
+ event: ExternalEvent,
310
+ calendarId: string
311
+ ): Promise<void> {
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
+ };
321
+
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
+ });
329
+ }
330
+
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);
346
+ }
347
+
348
+ async stopSync(calendarId: string): Promise<void> {
349
+ const authToken = await this.get<string>("auth_token");
350
+ await this.tools.calendarTool.stopSync(authToken, calendarId);
351
+ }
352
+ ```
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(
287
360
  incomingActivity: NewActivityWithNotes,
288
361
  calendarId: string
289
362
  ): Promise<void> {
@@ -320,11 +393,6 @@ async handleEvent(
320
393
  id: activityId,
321
394
  });
322
395
  }
323
-
324
- async stopSync(calendarId: string): Promise<void> {
325
- const authToken = await this.get<string>("auth_token");
326
- await this.tools.calendarTool.stopSync(authToken, calendarId);
327
- }
328
396
  ```
329
397
 
330
398
  ## Calendar Selection Pattern
@@ -419,24 +487,18 @@ async syncBatch(args: any, resourceId: string): Promise<void> {
419
487
  // Process one batch (keep under time limit)
420
488
  const result = await this.fetchBatch(state.nextPageToken);
421
489
 
422
- // Process results (create activities with Notes)
490
+ // Process results using source/key pattern (automatic upserts, no manual tracking)
423
491
  for (const item of result.items) {
424
- // Check if already synced
425
- const mappingKey = `item_mapping:${resourceId}:${item.id}`;
426
- const existingId = await this.get<Uuid>(mappingKey);
427
-
428
- if (!existingId) {
429
- // New item - generate UUID and store mapping
430
- const activityId = Uuid.Generate();
431
- await this.set(mappingKey, activityId);
432
-
433
- await this.tools.plot.createActivity({
434
- id: activityId,
435
- type: ActivityType.Note,
436
- title: item.title,
437
- notes: [{ id: Uuid.Generate(), content: item.description }],
438
- });
439
- }
492
+ await this.tools.plot.createActivity({
493
+ source: item.url, // Use item's canonical URL for automatic deduplication
494
+ type: ActivityType.Note,
495
+ title: item.title,
496
+ notes: [{
497
+ activity: { source: item.url },
498
+ key: "description", // Use key for upsertable notes
499
+ content: item.description,
500
+ }],
501
+ });
440
502
  }
441
503
 
442
504
  if (result.nextPageToken) {
@@ -495,9 +557,9 @@ try {
495
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.
496
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.
497
559
  - **Always create Activities with Notes** - See "Understanding Activities and Notes" section above for the thread/message pattern and decision tree.
498
- - **Use correct Activity types** - Most should be `ActivityType.Note`. Only use `Action` for tasks with `doneAt`, and `Event` for items with `start`/`end`.
499
- - **Track external items with UUID mappings** - Generate UUIDs with `Uuid.Generate()` and store mappings (`external_id uuid`) for deduplication. Never rely on the `source` field.
500
- - **Add Notes to existing Activities** - Look up stored UUID mappings before creating new Activities. Think thread replies, not new threads.
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.
501
563
  - Tools are declared in the `build` method and accessed via `this.tools.toolName` in twist methods.
502
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.
503
565
  - **Always use Callbacks tool for persistent references** - Direct function references don't survive worker restarts.
@@ -27,10 +27,30 @@ 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. **Track external items using generated UUIDs** - Store mappings between external IDs and Plot UUIDs for deduplication
31
- 4. **Most Activities should be `ActivityType.Note`** - Use `Action` only for tasks with `doneAt`, use `Event` only for items with `start`/`end`
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?
@@ -269,7 +289,9 @@ async onAuthComplete(authResult: { authToken: string }, provider: string) {
269
289
 
270
290
  ## Sync Pattern
271
291
 
272
- Pattern for syncing external data - demonstrates adding Notes to existing Activities:
292
+ ### Recommended: Upsert via Source/Key (Strategy 2)
293
+
294
+ Pattern for syncing external data using automatic upserts - **no manual ID tracking needed**:
273
295
 
274
296
  ```typescript
275
297
  async startSync(calendarId: string): Promise<void> {
@@ -284,6 +306,57 @@ async startSync(calendarId: string): Promise<void> {
284
306
  }
285
307
 
286
308
  async handleEvent(
309
+ event: ExternalEvent,
310
+ calendarId: string
311
+ ): Promise<void> {
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
+ };
321
+
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
+ });
329
+ }
330
+
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);
346
+ }
347
+
348
+ async stopSync(calendarId: string): Promise<void> {
349
+ const authToken = await this.get<string>("auth_token");
350
+ await this.tools.calendarTool.stopSync(authToken, calendarId);
351
+ }
352
+ ```
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(
287
360
  incomingActivity: NewActivityWithNotes,
288
361
  calendarId: string
289
362
  ): Promise<void> {
@@ -320,11 +393,6 @@ async handleEvent(
320
393
  id: activityId,
321
394
  });
322
395
  }
323
-
324
- async stopSync(calendarId: string): Promise<void> {
325
- const authToken = await this.get<string>("auth_token");
326
- await this.tools.calendarTool.stopSync(authToken, calendarId);
327
- }
328
396
  ```
329
397
 
330
398
  ## Calendar Selection Pattern
@@ -419,24 +487,18 @@ async syncBatch(args: any, resourceId: string): Promise<void> {
419
487
  // Process one batch (keep under time limit)
420
488
  const result = await this.fetchBatch(state.nextPageToken);
421
489
 
422
- // Process results (create activities with Notes)
490
+ // Process results using source/key pattern (automatic upserts, no manual tracking)
423
491
  for (const item of result.items) {
424
- // Check if already synced
425
- const mappingKey = `item_mapping:${resourceId}:${item.id}`;
426
- const existingId = await this.get<Uuid>(mappingKey);
427
-
428
- if (!existingId) {
429
- // New item - generate UUID and store mapping
430
- const activityId = Uuid.Generate();
431
- await this.set(mappingKey, activityId);
432
-
433
- await this.tools.plot.createActivity({
434
- id: activityId,
435
- type: ActivityType.Note,
436
- title: item.title,
437
- notes: [{ id: Uuid.Generate(), content: item.description }],
438
- });
439
- }
492
+ await this.tools.plot.createActivity({
493
+ source: item.url, // Use item's canonical URL for automatic deduplication
494
+ type: ActivityType.Note,
495
+ title: item.title,
496
+ notes: [{
497
+ activity: { source: item.url },
498
+ key: "description", // Use key for upsertable notes
499
+ content: item.description,
500
+ }],
501
+ });
440
502
  }
441
503
 
442
504
  if (result.nextPageToken) {
@@ -495,9 +557,9 @@ try {
495
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.
496
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.
497
559
  - **Always create Activities with Notes** - See "Understanding Activities and Notes" section above for the thread/message pattern and decision tree.
498
- - **Use correct Activity types** - Most should be `ActivityType.Note`. Only use `Action` for tasks with `doneAt`, and `Event` for items with `start`/`end`.
499
- - **Track external items with UUID mappings** - Generate UUIDs with `Uuid.Generate()` and store mappings (`external_id uuid`) for deduplication. Never rely on the `source` field.
500
- - **Add Notes to existing Activities** - Look up stored UUID mappings before creating new Activities. Think thread replies, not new threads.
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.
501
563
  - Tools are declared in the `build` method and accessed via `this.tools.toolName` in twist methods.
502
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.
503
565
  - **Always use Callbacks tool for persistent references** - Direct function references don't survive worker restarts.
@@ -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
  *
@@ -89,21 +93,10 @@ export interface SyncOptions {
89
93
  * syncUpdate: SyncUpdate,
90
94
  * syncMeta: { initialSync: boolean }
91
95
  * ) {
92
- * // Step 4: Process synced events
93
- * if ('activityId' in syncUpdate) {
94
- * // Update existing activity
95
- * if (syncUpdate.update) {
96
- * await this.plot.updateActivity(syncUpdate.update);
97
- * }
98
- * if (syncUpdate.notes) {
99
- * for (const note of syncUpdate.notes) {
100
- * await this.plot.createNote(note);
101
- * }
102
- * }
103
- * } else {
104
- * // Create new activity
105
- * await this.plot.createActivity(syncUpdate);
106
- * }
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);
107
100
  * }
108
101
  * }
109
102
  * ```
@@ -136,13 +129,17 @@ export interface CalendarTool {
136
129
  * event import and ongoing change notifications. The callback function
137
130
  * will be invoked for each synced event.
138
131
  *
139
- * Tools implementing this should:
140
- * - Generate UUIDs for new activities and notes using Uuid.Generate()
141
- * - Track activity IDs locally to detect updates vs new items
142
- * - Send NewActivityWithNotes for new events
143
- * - Send update object with activityId for changed events
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)
144
137
  * - Set activity.unread = false for initial sync, true for incremental updates
145
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
142
+ *
146
143
  * @param authToken - Authorization token for calendar access
147
144
  * @param calendarId - ID of the calendar to sync
148
145
  * @param callback - Function receiving (syncUpdate, ...extraArgs) for each synced event
@@ -1 +1 @@
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;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;;;;;;;;;;;;;;;;;;;;OAoBG;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
+ {"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"}
@@ -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
  /**
@@ -64,13 +68,18 @@ export interface MessagingTool {
64
68
  * Email threads and chat conversations are converted to SyncUpdate objects,
65
69
  * which can be either new items or updates to existing items.
66
70
  *
67
- * Tools implementing this should:
68
- * - Generate UUIDs for new activities and notes using Uuid.Generate()
69
- * - Track activity IDs locally to detect updates vs new threads
70
- * - Send NewActivityWithNotes for new threads
71
- * - Send update object with activityId and new notes for new messages in existing threads
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)
72
77
  * - Set activity.unread = false for initial sync, true for incremental updates
73
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
82
+ *
74
83
  * @param authToken - Authorization token for access
75
84
  * @param channelId - ID of the channel (e.g., channel, inbox) to sync
76
85
  * @param callback - Function receiving (syncUpdate, ...extraArgs) for each synced conversation
@@ -1 +1 @@
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;;;;;GAKG;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;;;;;;;;;;;;;;;;;;;OAmBG;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
+ {"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"}
@@ -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
  /**
@@ -64,14 +68,20 @@ export interface ProjectTool {
64
68
  * Issues and tasks are converted to SyncUpdate objects, which can be either
65
69
  * new items or updates to existing items.
66
70
  *
67
- * Tools implementing this should:
68
- * - Generate UUIDs for new activities and notes using Uuid.Generate()
69
- * - Track activity IDs locally to detect updates vs new items
70
- * - Send NewActivityWithNotes for new issues
71
- * - Send update object with activityId for changed issues or new comments
72
- * - Track description note ID and hash to detect description changes
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)
73
79
  * - Set activity.unread = false for initial sync, true for incremental updates
74
80
  *
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
84
+ *
75
85
  * @param authToken - Authorization token for access
76
86
  * @param projectId - ID of the project to sync
77
87
  * @param callback - Function receiving (syncUpdate, ...extraArgs) for each synced issue
@@ -95,10 +105,10 @@ export interface ProjectTool {
95
105
  * sync activity updates back to the external service.
96
106
  *
97
107
  * The update object contains only the fields that changed, plus id and source.
98
- * Uses the combination of start and doneAt to determine workflow state:
99
- * - doneAt set → Completed/Done state
100
- * - doneAt null + start set → In Progress/Active state
101
- * - doneAt null + start null → Backlog/Todo state
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
102
112
  *
103
113
  * @param authToken - Authorization token for access
104
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,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;;;;;GAKG;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;;;;;;;;;;;;;;;;;;;;OAoBG;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
+ {"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 = "eJyNkrFOwzAQht/l5qtRSJyzslWdsiAk2FCFTGpoFDdG9qEOVd4dOQFkJnvxYH/3f3fy3cA7xwG6F6qOCN68WzPw6OYA3Q2oiuesLwY66J+ds4AwjfMJuupeIXx5Cx0MVodgwh07Z8VKiTNfIrq+QAccTrtYttsuEIbzaE/ezNHbYN0orEmhbCuUirBtamwVIUlCInlcEKhJOilqpKCPBaFuVBp8HQOHXHR45ZUTG14goVSy7/MCPYp9nw+WbfpBB23tmx6mggGGX1T8FRXYFKXrMLP58HrblaxwTGiRlua1bVMn2gfDV+envHHeQPFTUOD5N96jdZyXfFrHIqL5eJJp/BM7b/L5IWJihQsMJNNd1qFkEzhiYoVzhmX5BnM3XJs="
1
+ window.hierarchyData = "eJyNkj1PwzAQhv/LzVfTJq2/toqpC0KCDVXIpIZGcWNkH+pQ5b8jJ4DMZC8e7Ofe506+GwTvKYJ+EZsjQrDvznbU+zGCvoHYpHM0FwsaDs/eO0AY+vEEetNIhK/gQEPnTIw23pH3js0UO9MlofMLaKB4WqWy1XKB0J17dwp2TN4ttlxhqxTuZIN8LZHzLYq1RCEkCsWPE4LYZp1UNVLRx4TQcpUHX/tIsRQdX2nm2IJXSFQu2R/KAtOz/aEcvJNNFnxvnHsz3VAxQPeLsr+iso2vZb4OI9mPYJZdKQr7jGZ5aYWW53//YOnqw1A2jgvIfgrKHvFvvEfnqSz5dJ5YQiviRR7/RD7Ycn5MGJvhCoPi+S6bWLMJlDA2wyXDNH0Dl09cpw=="