@plotday/twister 0.35.0 → 0.37.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 (262) hide show
  1. package/README.md +4 -2
  2. package/bin/commands/create.js +37 -3
  3. package/bin/commands/create.js.map +1 -1
  4. package/bin/commands/deploy.js +4 -0
  5. package/bin/commands/deploy.js.map +1 -1
  6. package/bin/index.js +1 -0
  7. package/bin/index.js.map +1 -1
  8. package/bin/templates/AGENTS.template.md +188 -271
  9. package/bin/templates/README.template.md +2 -23
  10. package/cli/templates/AGENTS.template.md +188 -271
  11. package/cli/templates/README.template.md +2 -23
  12. package/dist/docs/assets/hierarchy.js +1 -1
  13. package/dist/docs/assets/navigation.js +1 -1
  14. package/dist/docs/assets/search.js +1 -1
  15. package/dist/docs/classes/index.Options.html +10 -0
  16. package/dist/docs/classes/index.Source.html +184 -0
  17. package/dist/docs/classes/tool.ITool.html +1 -1
  18. package/dist/docs/classes/tool.Tool.html +21 -21
  19. package/dist/docs/classes/tools_ai.AI.html +1 -1
  20. package/dist/docs/classes/tools_callbacks.Callbacks.html +2 -2
  21. package/dist/docs/classes/tools_integrations.Integrations.html +38 -16
  22. package/dist/docs/classes/tools_network.Network.html +1 -1
  23. package/dist/docs/classes/tools_plot.Plot.html +86 -60
  24. package/dist/docs/classes/tools_store.Store.html +1 -1
  25. package/dist/docs/classes/tools_tasks.Tasks.html +1 -1
  26. package/dist/docs/classes/tools_twists.Twists.html +1 -1
  27. package/dist/docs/classes/twist.Twist.html +42 -10
  28. package/dist/docs/documents/Building_Sources.html +137 -0
  29. package/dist/docs/documents/Built-in_Tools.html +11 -2
  30. package/dist/docs/documents/Core_Concepts.html +5 -10
  31. package/dist/docs/documents/Getting_Started.html +1 -1
  32. package/dist/docs/enums/{plot.ActivityLinkType.html → plot.ActionType.html} +8 -8
  33. package/dist/docs/enums/plot.ActorType.html +7 -7
  34. package/dist/docs/enums/plot.ConferencingProvider.html +6 -6
  35. package/dist/docs/enums/plot.ThemeColor.html +9 -9
  36. package/dist/docs/enums/tag.Tag.html +3 -10
  37. package/dist/docs/enums/tools_integrations.AuthProvider.html +11 -11
  38. package/dist/docs/enums/tools_plot.ContactAccess.html +3 -3
  39. package/dist/docs/enums/tools_plot.PriorityAccess.html +3 -3
  40. package/dist/docs/enums/{tools_plot.ActivityAccess.html → tools_plot.ThreadAccess.html} +6 -6
  41. package/dist/docs/hierarchy.html +1 -1
  42. package/dist/docs/index.html +5 -6
  43. package/dist/docs/interfaces/utils_types.ToolShed.html +5 -5
  44. package/dist/docs/media/AGENTS.md +910 -0
  45. package/dist/docs/media/MULTI_USER_AUTH.md +111 -0
  46. package/dist/docs/media/SYNC_STRATEGIES.md +7 -7
  47. package/dist/docs/modules/index.html +1 -1
  48. package/dist/docs/modules/plot.html +1 -1
  49. package/dist/docs/modules/tool.html +1 -1
  50. package/dist/docs/modules/tools_integrations.html +1 -1
  51. package/dist/docs/modules/tools_plot.html +1 -1
  52. package/dist/docs/modules.html +1 -1
  53. package/dist/docs/types/index.BooleanDef.html +7 -0
  54. package/dist/docs/types/index.NewSchedule.html +33 -0
  55. package/dist/docs/types/index.NewScheduleContact.html +5 -0
  56. package/dist/docs/types/index.NewScheduleOccurrence.html +6 -0
  57. package/dist/docs/types/index.NumberDef.html +9 -0
  58. package/dist/docs/types/index.OptionDef.html +2 -0
  59. package/dist/docs/types/index.OptionsSchema.html +3 -0
  60. package/dist/docs/types/index.ResolvedOptions.html +4 -0
  61. package/dist/docs/types/index.Schedule.html +37 -0
  62. package/dist/docs/types/index.ScheduleContact.html +5 -0
  63. package/dist/docs/types/index.ScheduleContactRole.html +1 -0
  64. package/dist/docs/types/index.ScheduleContactStatus.html +1 -0
  65. package/dist/docs/types/index.ScheduleOccurrence.html +17 -0
  66. package/dist/docs/types/index.ScheduleOccurrenceUpdate.html +2 -0
  67. package/dist/docs/types/index.SelectDef.html +8 -0
  68. package/dist/docs/types/index.TextDef.html +8 -0
  69. package/dist/docs/types/plot.Action.html +26 -0
  70. package/dist/docs/types/plot.Actor.html +6 -6
  71. package/dist/docs/types/plot.ActorId.html +1 -1
  72. package/dist/docs/types/plot.ContentType.html +1 -1
  73. package/dist/docs/types/plot.Link.html +36 -0
  74. package/dist/docs/types/plot.NewActor.html +1 -1
  75. package/dist/docs/types/plot.NewContact.html +5 -5
  76. package/dist/docs/types/plot.NewLink.html +26 -0
  77. package/dist/docs/types/plot.NewLinkWithNotes.html +7 -0
  78. package/dist/docs/types/plot.NewNote.html +10 -10
  79. package/dist/docs/types/plot.NewPriority.html +1 -1
  80. package/dist/docs/types/plot.NewTags.html +1 -1
  81. package/dist/docs/types/plot.NewThread.html +27 -0
  82. package/dist/docs/types/plot.NewThreadWithNotes.html +1 -0
  83. package/dist/docs/types/plot.Note.html +9 -8
  84. package/dist/docs/types/plot.NoteUpdate.html +2 -2
  85. package/dist/docs/types/plot.PickPriorityConfig.html +8 -10
  86. package/dist/docs/types/plot.Priority.html +6 -6
  87. package/dist/docs/types/plot.PriorityUpdate.html +1 -1
  88. package/dist/docs/types/plot.Tags.html +1 -1
  89. package/dist/docs/types/plot.Thread.html +1 -0
  90. package/dist/docs/types/plot.ThreadCommon.html +17 -0
  91. package/dist/docs/types/plot.ThreadFilter.html +2 -0
  92. package/dist/docs/types/plot.ThreadMeta.html +11 -0
  93. package/dist/docs/types/plot.ThreadUpdate.html +3 -0
  94. package/dist/docs/types/plot.ThreadWithNotes.html +1 -0
  95. package/dist/docs/types/tools_integrations.ArchiveLinkFilter.html +11 -0
  96. package/dist/docs/types/tools_integrations.AuthToken.html +4 -4
  97. package/dist/docs/types/tools_integrations.Authorization.html +4 -4
  98. package/dist/docs/types/tools_integrations.Channel.html +11 -0
  99. package/dist/docs/types/tools_integrations.LinkTypeConfig.html +17 -0
  100. package/dist/docs/types/tools_plot.LinkFilter.html +10 -0
  101. package/dist/docs/types/tools_plot.NoteIntentHandler.html +6 -6
  102. package/dist/docs/types/tools_twists.TwistPermissions.html +1 -1
  103. package/dist/docs/types/utils_types.BuiltInTools.html +2 -2
  104. package/dist/docs/types/utils_types.ExtractBuildReturn.html +1 -1
  105. package/dist/docs/types/utils_types.InferOptions.html +1 -1
  106. package/dist/docs/types/utils_types.InferTools.html +1 -1
  107. package/dist/docs/types/utils_types.JSONValue.html +1 -1
  108. package/dist/docs/types/utils_types.PromiseValues.html +1 -1
  109. package/dist/docs/types/utils_types.ToolBuilder.html +2 -2
  110. package/dist/index.d.ts +3 -0
  111. package/dist/index.d.ts.map +1 -1
  112. package/dist/index.js +3 -0
  113. package/dist/index.js.map +1 -1
  114. package/dist/llm-docs/index.d.ts.map +1 -1
  115. package/dist/llm-docs/index.js +6 -8
  116. package/dist/llm-docs/index.js.map +1 -1
  117. package/dist/llm-docs/options.d.ts +9 -0
  118. package/dist/llm-docs/options.d.ts.map +1 -0
  119. package/dist/llm-docs/options.js +8 -0
  120. package/dist/llm-docs/options.js.map +1 -0
  121. package/dist/llm-docs/plot.d.ts +1 -1
  122. package/dist/llm-docs/plot.d.ts.map +1 -1
  123. package/dist/llm-docs/plot.js +1 -1
  124. package/dist/llm-docs/plot.js.map +1 -1
  125. package/dist/llm-docs/schedule.d.ts +9 -0
  126. package/dist/llm-docs/schedule.d.ts.map +1 -0
  127. package/dist/llm-docs/schedule.js +8 -0
  128. package/dist/llm-docs/schedule.js.map +1 -0
  129. package/dist/llm-docs/source.d.ts +9 -0
  130. package/dist/llm-docs/source.d.ts.map +1 -0
  131. package/dist/llm-docs/source.js +8 -0
  132. package/dist/llm-docs/source.js.map +1 -0
  133. package/dist/llm-docs/tag.d.ts +1 -1
  134. package/dist/llm-docs/tag.d.ts.map +1 -1
  135. package/dist/llm-docs/tag.js +1 -1
  136. package/dist/llm-docs/tag.js.map +1 -1
  137. package/dist/llm-docs/tool.d.ts +1 -1
  138. package/dist/llm-docs/tool.d.ts.map +1 -1
  139. package/dist/llm-docs/tool.js +1 -1
  140. package/dist/llm-docs/tool.js.map +1 -1
  141. package/dist/llm-docs/tools/callbacks.d.ts +1 -1
  142. package/dist/llm-docs/tools/callbacks.d.ts.map +1 -1
  143. package/dist/llm-docs/tools/callbacks.js +1 -1
  144. package/dist/llm-docs/tools/callbacks.js.map +1 -1
  145. package/dist/llm-docs/tools/integrations.d.ts +1 -1
  146. package/dist/llm-docs/tools/integrations.d.ts.map +1 -1
  147. package/dist/llm-docs/tools/integrations.js +1 -1
  148. package/dist/llm-docs/tools/integrations.js.map +1 -1
  149. package/dist/llm-docs/tools/plot.d.ts +1 -1
  150. package/dist/llm-docs/tools/plot.d.ts.map +1 -1
  151. package/dist/llm-docs/tools/plot.js +1 -1
  152. package/dist/llm-docs/tools/plot.js.map +1 -1
  153. package/dist/llm-docs/tools/twists.d.ts +1 -1
  154. package/dist/llm-docs/tools/twists.d.ts.map +1 -1
  155. package/dist/llm-docs/tools/twists.js +1 -1
  156. package/dist/llm-docs/tools/twists.js.map +1 -1
  157. package/dist/llm-docs/twist-guide-template.d.ts +1 -1
  158. package/dist/llm-docs/twist-guide-template.d.ts.map +1 -1
  159. package/dist/llm-docs/twist-guide-template.js +1 -1
  160. package/dist/llm-docs/twist-guide-template.js.map +1 -1
  161. package/dist/llm-docs/twist.d.ts +1 -1
  162. package/dist/llm-docs/twist.d.ts.map +1 -1
  163. package/dist/llm-docs/twist.js +1 -1
  164. package/dist/llm-docs/twist.js.map +1 -1
  165. package/dist/options.d.ts +104 -0
  166. package/dist/options.d.ts.map +1 -0
  167. package/dist/options.js +40 -0
  168. package/dist/options.js.map +1 -0
  169. package/dist/plot.d.ts +254 -588
  170. package/dist/plot.d.ts.map +1 -1
  171. package/dist/plot.js +16 -49
  172. package/dist/plot.js.map +1 -1
  173. package/dist/schedule.d.ts +172 -0
  174. package/dist/schedule.d.ts.map +1 -0
  175. package/dist/schedule.js +2 -0
  176. package/dist/schedule.js.map +1 -0
  177. package/dist/source.d.ts +133 -0
  178. package/dist/source.d.ts.map +1 -0
  179. package/dist/source.js +116 -0
  180. package/dist/source.js.map +1 -0
  181. package/dist/tag.d.ts +3 -10
  182. package/dist/tag.d.ts.map +1 -1
  183. package/dist/tag.js +2 -11
  184. package/dist/tag.js.map +1 -1
  185. package/dist/tool.d.ts +1 -15
  186. package/dist/tool.d.ts.map +1 -1
  187. package/dist/tool.js.map +1 -1
  188. package/dist/tools/callbacks.d.ts +1 -1
  189. package/dist/tools/callbacks.js +1 -1
  190. package/dist/tools/integrations.d.ts +107 -50
  191. package/dist/tools/integrations.d.ts.map +1 -1
  192. package/dist/tools/integrations.js +23 -22
  193. package/dist/tools/integrations.js.map +1 -1
  194. package/dist/tools/plot.d.ts +133 -117
  195. package/dist/tools/plot.d.ts.map +1 -1
  196. package/dist/tools/plot.js +19 -21
  197. package/dist/tools/plot.js.map +1 -1
  198. package/dist/tools/twists.d.ts +1 -1
  199. package/dist/twist-guide.d.ts +1 -1
  200. package/dist/twist-guide.d.ts.map +1 -1
  201. package/dist/twist.d.ts +66 -11
  202. package/dist/twist.d.ts.map +1 -1
  203. package/dist/twist.js +79 -10
  204. package/dist/twist.js.map +1 -1
  205. package/dist/utils/types.d.ts +5 -1
  206. package/dist/utils/types.d.ts.map +1 -1
  207. package/package.json +16 -41
  208. package/dist/common/calendar.d.ts +0 -135
  209. package/dist/common/calendar.d.ts.map +0 -1
  210. package/dist/common/calendar.js +0 -2
  211. package/dist/common/calendar.js.map +0 -1
  212. package/dist/common/documents.d.ts +0 -122
  213. package/dist/common/documents.d.ts.map +0 -1
  214. package/dist/common/documents.js +0 -2
  215. package/dist/common/documents.js.map +0 -1
  216. package/dist/common/messaging.d.ts +0 -84
  217. package/dist/common/messaging.d.ts.map +0 -1
  218. package/dist/common/messaging.js +0 -2
  219. package/dist/common/messaging.js.map +0 -1
  220. package/dist/common/projects.d.ts +0 -112
  221. package/dist/common/projects.d.ts.map +0 -1
  222. package/dist/common/projects.js +0 -2
  223. package/dist/common/projects.js.map +0 -1
  224. package/dist/docs/documents/Building_Custom_Tools.html +0 -215
  225. package/dist/docs/enums/plot.ActivityKind.html +0 -14
  226. package/dist/docs/enums/plot.ActivityType.html +0 -10
  227. package/dist/docs/modules/common_calendar.html +0 -1
  228. package/dist/docs/types/common_calendar.Calendar.html +0 -13
  229. package/dist/docs/types/common_calendar.CalendarTool.html +0 -81
  230. package/dist/docs/types/common_calendar.SyncOptions.html +0 -24
  231. package/dist/docs/types/plot.Activity.html +0 -2
  232. package/dist/docs/types/plot.ActivityCommon.html +0 -20
  233. package/dist/docs/types/plot.ActivityFilter.html +0 -3
  234. package/dist/docs/types/plot.ActivityLink.html +0 -26
  235. package/dist/docs/types/plot.ActivityMeta.html +0 -11
  236. package/dist/docs/types/plot.ActivityOccurrence.html +0 -22
  237. package/dist/docs/types/plot.ActivityOccurrenceUpdate.html +0 -3
  238. package/dist/docs/types/plot.ActivityUpdate.html +0 -3
  239. package/dist/docs/types/plot.ActivityWithNotes.html +0 -1
  240. package/dist/docs/types/plot.NewActivity.html +0 -81
  241. package/dist/docs/types/plot.NewActivityOccurrence.html +0 -24
  242. package/dist/docs/types/plot.NewActivityWithNotes.html +0 -1
  243. package/dist/docs/types/tool.SyncToolOptions.html +0 -9
  244. package/dist/docs/types/tools_integrations.IntegrationOptions.html +0 -4
  245. package/dist/docs/types/tools_integrations.IntegrationProviderConfig.html +0 -13
  246. package/dist/docs/types/tools_integrations.Syncable.html +0 -7
  247. package/dist/llm-docs/common/calendar.d.ts +0 -9
  248. package/dist/llm-docs/common/calendar.d.ts.map +0 -1
  249. package/dist/llm-docs/common/calendar.js +0 -8
  250. package/dist/llm-docs/common/calendar.js.map +0 -1
  251. package/dist/llm-docs/common/documents.d.ts +0 -9
  252. package/dist/llm-docs/common/documents.d.ts.map +0 -1
  253. package/dist/llm-docs/common/documents.js +0 -8
  254. package/dist/llm-docs/common/documents.js.map +0 -1
  255. package/dist/llm-docs/common/messaging.d.ts +0 -9
  256. package/dist/llm-docs/common/messaging.d.ts.map +0 -1
  257. package/dist/llm-docs/common/messaging.js +0 -8
  258. package/dist/llm-docs/common/messaging.js.map +0 -1
  259. package/dist/llm-docs/common/projects.d.ts +0 -9
  260. package/dist/llm-docs/common/projects.d.ts.map +0 -1
  261. package/dist/llm-docs/common/projects.js +0 -8
  262. package/dist/llm-docs/common/projects.js.map +0 -1
@@ -19,79 +19,78 @@ Plot Twists are TypeScript classes that extend the `Twist` base class. Twists in
19
19
  - **Store intermediate state**: Use the Store tool to persist state between batches
20
20
  - **Examples**: Syncing large datasets, processing many API calls, or performing batch operations
21
21
 
22
- ## Understanding Activities and Notes
22
+ ## Understanding Threads and Notes
23
23
 
24
- **CRITICAL CONCEPT**: An **Activity** represents something done or to be done (a task, event, or conversation), while **Notes** represent the updates and details on that activity.
24
+ **CRITICAL CONCEPT**: A **Thread** represents something done or to be done (a task, event, or conversation), while **Notes** represent the updates and details on that thread.
25
25
 
26
- **Think of an Activity as a thread** on a messaging platform, and **Notes as the messages in that thread**.
26
+ **Think of a Thread as a thread** on a messaging platform, and **Notes as the messages in that thread**.
27
27
 
28
28
  ### Key Guidelines
29
29
 
30
- 1. **Always create Activities with an initial Note** - The title is just a summary; detailed content goes in Notes
31
- 2. **Add Notes to existing Activities for updates** - Don't create a new Activity for each related message
32
- 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.
33
- 4. **For advanced cases, use generated UUIDs** - Only when you need multiple Plot activities per external item (see SYNC_STRATEGIES.md)
34
- 5. **Most Activities should be `ActivityType.Note`** - Use `Action` only for tasks with `done`, use `Event` only for items with `start`/`end`
30
+ 1. **Always create Threads with an initial Note** - The title is just a summary; detailed content goes in Notes
31
+ 2. **Add Notes to existing Threads for updates** - Don't create a new Thread for each related message
32
+ 3. **Use Thread.source and Note.key for automatic upserts (Recommended)** - Set Thread.source to the external item's URL for deduplication, and use Note.key for upsertable note content. No manual ID tracking needed.
33
+ 4. **For advanced cases, use generated UUIDs** - Only when you need multiple Plot threads per external item (see SYNC_STRATEGIES.md)
34
+ 5. **Most Threads should be `ThreadType.Note`** - Use `Action` only for tasks with `done`, use `Event` only for items with `start`/`end`
35
35
 
36
36
  ### Recommended Decision Tree (Strategy 2: Upsert via Source/Key)
37
37
 
38
38
  ```
39
39
  New event/task/conversation from external system?
40
40
  ├─ Has stable URL or ID?
41
- │ └─ Yes → Set Activity.source to the canonical URL/ID
42
- │ Create Activity (Plot handles deduplication automatically)
41
+ │ └─ Yes → Set Thread.source to the canonical URL/ID
42
+ │ Create Thread (Plot handles deduplication automatically)
43
43
  │ Use Note.key for different note types:
44
44
  │ - "description" for main content
45
45
  │ - "metadata" for status/priority/assignee
46
46
  │ - "comment-{id}" for individual comments
47
47
 
48
- └─ No stable identifier OR need multiple Plot activities per external item?
48
+ └─ No stable identifier OR need multiple Plot threads per external item?
49
49
  └─ Use Advanced Pattern (Strategy 3: Generate and Store IDs)
50
50
  See SYNC_STRATEGIES.md for details
51
51
  ```
52
52
 
53
53
  ### Advanced Decision Tree (Strategy 3: Generate and Store IDs)
54
54
 
55
- Only use when source/key upserts aren't sufficient (e.g., creating multiple activities from one external item):
55
+ Only use when source/key upserts aren't sufficient (e.g., creating multiple threads from one external item):
56
56
 
57
57
  ```
58
58
  New event/task/conversation?
59
59
  ├─ Yes → Generate UUID with Uuid.Generate()
60
- │ Create new Activity with that UUID
61
- │ Store mapping: external_id → activity_uuid
60
+ │ Create new Thread with that UUID
61
+ │ Store mapping: external_id → thread_uuid
62
62
 
63
63
  └─ No (update/reply/comment) → Look up mapping by external_id
64
- ├─ Found → Add Note to existing Activity using stored UUID
65
- └─ Not found → Create new Activity with UUID + store mapping
64
+ ├─ Found → Add Note to existing Thread using stored UUID
65
+ └─ Not found → Create new Thread with UUID + store mapping
66
66
  ```
67
67
 
68
68
  ## Twist Structure Pattern
69
69
 
70
70
  ```typescript
71
71
  import {
72
- type Activity,
72
+ type Thread,
73
+ type NewThreadWithNotes,
74
+ type ThreadFilter,
73
75
  type Priority,
74
76
  type ToolBuilder,
75
- twist,
77
+ Twist,
78
+ ThreadType,
76
79
  } from "@plotday/twister";
77
- import { Plot } from "@plotday/twister/tools/plot";
78
- import { Uuid } from "@plotday/twister/utils/uuid";
80
+ import { ThreadAccess, Plot } from "@plotday/twister/tools/plot";
81
+ // Import your sources or tools as needed
79
82
 
80
83
  export default class MyTwist extends Twist<MyTwist> {
81
84
  build(build: ToolBuilder) {
82
85
  return {
83
- plot: build(Plot),
86
+ plot: build(Plot, {
87
+ thread: { access: ThreadAccess.Create },
88
+ }),
84
89
  };
85
90
  }
86
91
 
87
- async activate(priority: Pick<Priority, "id">) {
88
- // Called when twist is enabled for a priority
89
- // Common actions: request auth, create setup activities
90
- }
91
-
92
- async activity(activity: Activity) {
93
- // Called when an activity is routed to this twist
94
- // Common actions: process external events, update activities
92
+ async activate(_priority: Pick<Priority, "id">) {
93
+ // Auth and resource selection handled in the twist edit modal.
95
94
  }
96
95
  }
97
96
  ```
@@ -132,238 +131,197 @@ For complete API documentation of built-in tools including all methods, types, a
132
131
 
133
132
  **Critical**: Never use instance variables for state. They are lost after function execution. Always use Store methods.
134
133
 
135
- ### External Tools (Add to package.json)
136
-
137
- Add tool dependencies to `package.json`:
138
-
139
- ```json
140
- {
141
- "dependencies": {
142
- "@plotday/twister": "workspace:^",
143
- "@plotday/tool-google-calendar": "workspace:^"
144
- }
145
- }
146
- ```
147
-
148
- #### Common External Tools
149
-
150
- - `@plotday/tool-google-calendar`: Google Calendar integration
151
- - `@plotday/tool-outlook-calendar`: Outlook Calendar integration
152
- - `@plotday/tool-google-contacts`: Google Contacts integration
153
-
154
134
  ## Lifecycle Methods
155
135
 
156
136
  ### activate(priority: Pick<Priority, "id">)
157
137
 
158
- Called when the twist is enabled for a priority. Common patterns:
138
+ Called when the twist is enabled for a priority. Auth and resource selection are handled automatically via the twist edit modal when using external tools with Integrations.
159
139
 
160
- **Request Authentication:**
140
+ Most twists have an empty or minimal `activate()`:
161
141
 
162
142
  ```typescript
163
143
  async activate(_priority: Pick<Priority, "id">) {
164
- const authLink = await this.tools.externalTool.requestAuth(
165
- this.onAuthComplete,
166
- "google"
167
- );
168
-
169
- await this.tools.plot.createActivity({
170
- type: ActivityType.Note,
171
- title: "Connect your account",
172
- notes: [
173
- {
174
- content: "Click the link below to connect your account and start syncing.",
175
- links: [authLink],
176
- },
177
- ],
178
- });
144
+ // Auth and resource selection are handled in the twist edit modal.
145
+ // Only add custom initialization here if needed.
179
146
  }
180
147
  ```
181
148
 
182
- **Store Parent Activity for Later:**
149
+ **Store Parent Thread for Later (optional):**
183
150
 
184
151
  ```typescript
185
- const activity = await this.tools.plot.createActivity({
186
- type: ActivityType.Note,
187
- title: "Setup",
188
- notes: [
189
- {
190
- content: "Your twist is being set up. Configuration steps will appear here.",
191
- },
192
- ],
193
- });
194
-
195
- await this.set("setup_activity_id", activity.id);
152
+ async activate(_priority: Pick<Priority, "id">) {
153
+ const threadId = await this.tools.plot.createThread({
154
+ type: ThreadType.Note,
155
+ title: "Setup complete",
156
+ notes: [{
157
+ content: "Your twist is ready. Threads will appear as they sync.",
158
+ }],
159
+ });
160
+ await this.set("setup_thread_id", threadId);
161
+ }
196
162
  ```
197
163
 
198
- ### activity(activity: Activity)
164
+ ### Event Callbacks (via build options)
199
165
 
200
- Called when an activity is routed to the twist. Common patterns:
166
+ Twists respond to events through callbacks declared in `build()`:
201
167
 
202
- **Create Activities from External Events:**
168
+ **React to thread changes (for two-way sync):**
203
169
 
204
170
  ```typescript
205
- async activity(activity: Activity) {
206
- await this.tools.plot.createActivity(activity);
171
+ plot: build(Plot, {
172
+ thread: {
173
+ access: ThreadAccess.Create,
174
+ updated: this.onThreadUpdated,
175
+ },
176
+ note: {
177
+ created: this.onNoteCreated,
178
+ },
179
+ }),
180
+
181
+ async onThreadUpdated(thread: Thread, changes: { tagsAdded, tagsRemoved }): Promise<void> {
182
+ const tool = this.getToolForThread(thread);
183
+ if (tool?.updateIssue) await tool.updateIssue(thread);
184
+ }
185
+
186
+ async onNoteCreated(note: Note): Promise<void> {
187
+ if (note.author.type === ActorType.Twist) return; // Prevent loops
188
+ // Sync note to external service as a comment
207
189
  }
208
190
  ```
209
191
 
210
- **Update Based on User Action:**
192
+ **Respond to mentions (AI twist pattern):**
211
193
 
212
194
  ```typescript
213
- async activity(activity: Activity) {
214
- if (activity.completed) {
215
- await this.handleCompletion(activity);
216
- }
217
- }
195
+ plot: build(Plot, {
196
+ thread: { access: ThreadAccess.Respond },
197
+ note: {
198
+ intents: [{
199
+ description: "Respond to general questions",
200
+ examples: ["What's the weather?", "Help me plan my week"],
201
+ handler: this.respond,
202
+ }],
203
+ },
204
+ }),
218
205
  ```
219
206
 
220
- ## Activity Links
207
+ ## Actions
221
208
 
222
- Activity links enable user interaction:
209
+ Actions enable user interaction:
223
210
 
224
211
  ```typescript
225
- import { type ActivityLink, ActivityLinkType } from "@plotday/twister";
212
+ import { type Action, ActionType } from "@plotday/twister";
226
213
 
227
- // URL link
228
- const urlLink: ActivityLink = {
214
+ // External URL action
215
+ const urlAction: Action = {
229
216
  title: "Open website",
230
- type: ActivityLinkType.url,
217
+ type: ActionType.external,
231
218
  url: "https://example.com",
232
219
  };
233
220
 
234
- // Callback link (uses Callbacks tool)
235
- const token = await this.callback(this.onLinkClicked, "context");
236
- const callbackLink: ActivityLink = {
221
+ // Callback action (uses Callbacks tool — use linkCallback, not callback)
222
+ const token = await this.linkCallback(this.onActionClicked, "context");
223
+ const callbackAction: Action = {
237
224
  title: "Click me",
238
- type: ActivityLinkType.callback,
239
- token: token,
225
+ type: ActionType.callback,
226
+ callback: token,
240
227
  };
241
228
 
242
- // Add to activity note
243
- await this.tools.plot.createActivity({
244
- type: ActivityType.Note,
245
- title: "Task with links",
229
+ // Add to thread note
230
+ await this.tools.plot.createThread({
231
+ type: ThreadType.Note,
232
+ title: "Task with actions",
246
233
  notes: [
247
234
  {
248
- content: "Click the links below to take action.",
249
- links: [urlLink, callbackLink],
235
+ content: "Click the actions below to take action.",
236
+ actions: [urlAction, callbackAction],
250
237
  },
251
238
  ],
252
239
  });
240
+
241
+ // Callback handler receives the Action as first argument
242
+ async onActionClicked(action: Action, context: string): Promise<void> {
243
+ // Handle action click
244
+ }
253
245
  ```
254
246
 
255
247
  ## Authentication Pattern
256
248
 
257
- Common pattern for OAuth authentication:
249
+ Auth is handled automatically via the Integrations tool. Tools declare their OAuth provider in `build()`, and users connect in the twist edit modal. **You do not need to create auth activities manually.**
258
250
 
259
251
  ```typescript
260
- async activate(_priority: Pick<Priority, "id">) {
261
- // Request auth link from tool with callback
262
- const authLink = await this.tools.googleTool.requestAuth(
263
- this.onAuthComplete,
264
- "google"
265
- );
266
-
267
- // Create activity with auth link
268
- const activity = await this.tools.plot.createActivity({
269
- type: ActivityType.Note,
270
- title: "Connect Google account",
271
- notes: [
272
- {
273
- content: "Click below to connect your Google account and start syncing.",
274
- links: [authLink],
275
- },
276
- ],
277
- });
278
-
279
- // Store for later use
280
- await this.set("auth_activity_id", activity.id);
252
+ // In your tool's build() method:
253
+ build(build: ToolBuilder) {
254
+ return {
255
+ integrations: build(Integrations, {
256
+ providers: [{
257
+ provider: AuthProvider.Google,
258
+ scopes: ["https://www.googleapis.com/auth/calendar"],
259
+ getChannels: this.getChannels, // List available resources after auth
260
+ onChannelEnabled: this.onChannelEnabled, // User enabled a resource
261
+ onChannelDisabled: this.onChannelDisabled, // User disabled a resource
262
+ }],
263
+ }),
264
+ // ...
265
+ };
281
266
  }
282
267
 
283
- async onAuthComplete(authResult: { authToken: string }, provider: string) {
284
- // Store auth token
285
- await this.set(`${provider}_auth`, authResult.authToken);
268
+ // Get a token for API calls:
269
+ const token = await this.tools.integrations.get(AuthProvider.Google, channelId);
270
+ if (!token) throw new Error("No auth token available");
271
+ const client = new ApiClient({ accessToken: token.token });
272
+ ```
286
273
 
287
- // Continue setup flow
288
- await this.setupSyncOptions(authResult.authToken);
289
- }
274
+ For per-user write-backs (e.g., RSVP, comments attributed to the acting user):
275
+
276
+ ```typescript
277
+ await this.tools.integrations.actAs(
278
+ AuthProvider.Google,
279
+ actorId, // The user who performed the action
280
+ threadId, // Thread to prompt for auth if needed
281
+ this.performWriteBack,
282
+ ...extraArgs
283
+ );
290
284
  ```
291
285
 
292
286
  ## Sync Pattern
293
287
 
294
- ### Recommended: Upsert via Source/Key (Strategy 2)
288
+ ### Upsert via Source/Key (Strategy 2)
295
289
 
296
- Pattern for syncing external data using automatic upserts - **no manual ID tracking needed**:
290
+ Use source/key for automatic upserts:
297
291
 
298
292
  ```typescript
299
- async startSync(calendarId: string): Promise<void> {
300
- const authToken = await this.get<string>("auth_token");
301
-
302
- await this.tools.calendarTool.startSync(
303
- authToken,
304
- calendarId,
305
- this.handleEvent,
306
- calendarId
307
- );
308
- }
309
-
310
- async handleEvent(
311
- event: ExternalEvent,
312
- calendarId: string
313
- ): Promise<void> {
314
- // Use the event's canonical URL as the source for automatic deduplication
315
- const activity: NewActivityWithNotes = {
316
- source: event.htmlLink, // or event.url, depending on your external system
317
- type: ActivityType.Event,
293
+ async handleEvent(event: ExternalEvent): Promise<void> {
294
+ const thread: NewThreadWithNotes = {
295
+ source: event.htmlLink, // Canonical URL for automatic deduplication
296
+ type: ThreadType.Event,
318
297
  title: event.summary || "(No title)",
319
- start: event.start?.dateTime || event.start?.date || null,
320
- end: event.end?.dateTime || event.end?.date || null,
321
298
  notes: [],
322
299
  };
323
300
 
324
- // Add description as an upsertable note
325
301
  if (event.description) {
326
- activity.notes.push({
327
- activity: { source: event.htmlLink },
328
- key: "description", // This key enables upserts - same key updates the note
302
+ thread.notes.push({
303
+ thread: { source: event.htmlLink },
304
+ key: "description", // This key enables note-level upserts
329
305
  content: event.description,
330
306
  });
331
307
  }
332
308
 
333
- // Add attendees as an upsertable note
334
- if (event.attendees?.length) {
335
- const attendeeList = event.attendees
336
- .map(a => `- ${a.email}${a.displayName ? ` (${a.displayName})` : ''}`)
337
- .join('\n');
338
-
339
- activity.notes.push({
340
- activity: { source: event.htmlLink },
341
- key: "attendees", // Different key for different note types
342
- content: `## Attendees\n${attendeeList}`,
343
- });
344
- }
345
-
346
- // Create or update - Plot automatically handles deduplication based on source
347
- await this.tools.plot.createActivity(activity);
348
- }
349
-
350
- async stopSync(calendarId: string): Promise<void> {
351
- const authToken = await this.get<string>("auth_token");
352
- await this.tools.calendarTool.stopSync(authToken, calendarId);
309
+ // Create or update Plot handles deduplication automatically
310
+ await this.tools.plot.createThread(thread);
353
311
  }
354
312
  ```
355
313
 
356
314
  ### Advanced: Generate and Store IDs (Strategy 3)
357
315
 
358
- 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.
316
+ Only use this pattern when you need to create multiple Plot threads from a single external item, or when the external system doesn't provide stable identifiers. See SYNC_STRATEGIES.md for details.
359
317
 
360
318
  ```typescript
361
319
  async handleEventAdvanced(
362
- incomingActivity: NewActivityWithNotes,
320
+ incomingThread: NewThreadWithNotes,
363
321
  calendarId: string
364
322
  ): Promise<void> {
365
323
  // Extract external event ID from meta (adapt based on your tool's data)
366
- const externalId = incomingActivity.meta?.eventId;
324
+ const externalId = incomingThread.meta?.eventId;
367
325
 
368
326
  if (!externalId) {
369
327
  console.error("Event missing external ID");
@@ -372,86 +330,45 @@ async handleEventAdvanced(
372
330
 
373
331
  // Check if we've already synced this event
374
332
  const mappingKey = `event_mapping:${calendarId}:${externalId}`;
375
- const existingActivityId = await this.get<Uuid>(mappingKey);
333
+ const existingThreadId = await this.get<Uuid>(mappingKey);
376
334
 
377
- if (existingActivityId) {
335
+ if (existingThreadId) {
378
336
  // Event already exists - add update as a Note (add message to thread)
379
- if (incomingActivity.notes?.[0]?.content) {
337
+ if (incomingThread.notes?.[0]?.content) {
380
338
  await this.tools.plot.createNote({
381
- activity: { id: existingActivityId },
382
- content: incomingActivity.notes[0].content,
339
+ thread: { id: existingThreadId },
340
+ content: incomingThread.notes[0].content,
383
341
  });
384
342
  }
385
343
  return;
386
344
  }
387
345
 
388
346
  // New event - generate UUID and store mapping
389
- const activityId = Uuid.Generate();
390
- await this.set(mappingKey, activityId);
347
+ const threadId = Uuid.Generate();
348
+ await this.set(mappingKey, threadId);
391
349
 
392
- // Create new Activity with initial Note (new thread with first message)
393
- await this.tools.plot.createActivity({
394
- ...incomingActivity,
395
- id: activityId,
350
+ // Create new Thread with initial Note (new thread with first message)
351
+ await this.tools.plot.createThread({
352
+ ...incomingThread,
353
+ id: threadId,
396
354
  });
397
355
  }
398
356
  ```
399
357
 
400
- ## Calendar Selection Pattern
358
+ ## Resource Selection
401
359
 
402
- Pattern for letting users select from multiple calendars/accounts:
360
+ Resource selection (calendars, projects, channels) is handled automatically in the twist edit modal via the Integrations tool. Users see a list of available resources returned by your tool's `getChannels()` method and toggle them on/off. You do **not** need to build custom selection UI.
403
361
 
404
362
  ```typescript
405
- private async createCalendarSelectionActivity(
406
- provider: string,
407
- calendars: Calendar[],
408
- authToken: string
409
- ): Promise<void> {
410
- const links: ActivityLink[] = [];
411
-
412
- for (const calendar of calendars) {
413
- const token = await this.callback(
414
- this.onCalendarSelected,
415
- provider,
416
- calendar.id,
417
- calendar.name,
418
- authToken
419
- );
420
-
421
- links.push({
422
- title: `📅 ${calendar.name}${calendar.primary ? " (Primary)" : ""}`,
423
- type: ActivityLinkType.callback,
424
- token: token,
425
- });
426
- }
427
-
428
- await this.tools.plot.createActivity({
429
- type: ActivityType.Note,
430
- title: "Which calendars would you like to connect?",
431
- notes: [
432
- {
433
- content: "Select the calendars you want to sync:",
434
- links,
435
- },
436
- ],
437
- });
438
- }
439
-
440
- async onCalendarSelected(
441
- link: ActivityLink,
442
- provider: string,
443
- calendarId: string,
444
- calendarName: string,
445
- authToken: string
446
- ): Promise<void> {
447
- // Start sync for selected calendar
448
- await this.tools.tool.startSync(
449
- authToken,
450
- calendarId,
451
- this.handleEvent,
452
- provider,
453
- calendarId
454
- );
363
+ // In your tool:
364
+ async getChannels(_auth: Authorization, token: AuthToken): Promise<Channel[]> {
365
+ const client = new ApiClient({ accessToken: token.token });
366
+ const calendars = await client.listCalendars();
367
+ return calendars.map(c => ({
368
+ id: c.id,
369
+ title: c.name,
370
+ children: c.subCalendars?.map(sc => ({ id: sc.id, title: sc.name })),
371
+ }));
455
372
  }
456
373
  ```
457
374
 
@@ -486,7 +403,7 @@ async startSync(resourceId: string): Promise<void> {
486
403
  await this.runTask(callback);
487
404
  }
488
405
 
489
- async syncBatch(args: any, resourceId: string): Promise<void> {
406
+ async syncBatch(resourceId: string): Promise<void> {
490
407
  // Load state from Store (set by previous execution)
491
408
  const state = await this.get(`sync_state_${resourceId}`);
492
409
 
@@ -496,17 +413,17 @@ async syncBatch(args: any, resourceId: string): Promise<void> {
496
413
  // Process results using source/key pattern (automatic upserts, no manual tracking)
497
414
  // If each item makes ~10 requests, keep batch size ≤ 100 items to stay under limit
498
415
  for (const item of result.items) {
499
- // Each createActivity may make ~5-10 requests depending on notes/links
500
- await this.tools.plot.createActivity({
416
+ // Each createThread may make ~5-10 requests depending on notes/links
417
+ await this.tools.plot.createThread({
501
418
  source: item.url, // Use item's canonical URL for automatic deduplication
502
- type: ActivityType.Note,
419
+ type: ThreadType.Note,
503
420
  title: item.title,
504
421
  notes: [{
505
422
  activity: { source: item.url },
506
423
  key: "description", // Use key for upsertable notes
507
424
  content: item.description,
508
425
  }],
509
- unread: !state.initialSync, // false for initial sync, true for incremental
426
+ ...(state.initialSync ? { unread: false } : {}), // false for initial, omit for incremental
510
427
  ...(state.initialSync ? { archived: false } : {}), // unarchive on initial only
511
428
  });
512
429
  }
@@ -528,8 +445,8 @@ async syncBatch(args: any, resourceId: string): Promise<void> {
528
445
  await this.clear(`sync_state_${resourceId}`);
529
446
 
530
447
  // Optionally notify user of completion
531
- await this.tools.plot.createActivity({
532
- type: ActivityType.Note,
448
+ await this.tools.plot.createThread({
449
+ type: ThreadType.Note,
533
450
  title: "Sync complete",
534
451
  notes: [
535
452
  {
@@ -541,9 +458,9 @@ async syncBatch(args: any, resourceId: string): Promise<void> {
541
458
  }
542
459
  ```
543
460
 
544
- ## Activity Sync Best Practices
461
+ ## Thread Sync Best Practices
545
462
 
546
- When syncing activities from external systems, follow these patterns for optimal user experience:
463
+ When syncing threads from external systems, follow these patterns for optimal user experience:
547
464
 
548
465
  ### The `initialSync` Flag
549
466
 
@@ -551,30 +468,30 @@ All sync-based tools should distinguish between initial sync (first import) and
551
468
 
552
469
  | Field | Initial Sync | Incremental Sync | Reason |
553
470
  |-------|--------------|------------------|---------|
554
- | `unread` | `false` | `true` | Avoid notification overload from historical items |
471
+ | `unread` | `false` | *omit* | Initial: mark read for all. Incremental: auto-mark read for author only |
555
472
  | `archived` | `false` | *omit* | Unarchive on install, preserve user choice on updates |
556
473
 
557
474
  **Example:**
558
475
  ```typescript
559
- const activity: NewActivity = {
560
- type: ActivityType.Event,
476
+ const thread: NewThread = {
477
+ type: ThreadType.Event,
561
478
  source: event.url,
562
479
  title: event.title,
563
- unread: !initialSync, // false for initial, true for incremental
564
- ...(initialSync ? { archived: false } : {}), // unarchive on initial only
480
+ ...(initialSync ? { unread: false } : {}), // false for initial, omit for incremental
481
+ ...(initialSync ? { archived: false } : {}), // unarchive on initial only
565
482
  };
566
483
  ```
567
484
 
568
485
  **Why this matters:**
569
- - **Initial sync**: Activities are unarchived and marked as read, preventing spam from bulk historical imports
570
- - **Incremental sync**: New activities appear as unread, and archived state is preserved (respects user's archiving decisions)
486
+ - **Initial sync**: Activities are unarchived and marked as read for all users, preventing spam from bulk historical imports
487
+ - **Incremental sync**: Activities are auto-marked read for the author (twist owner), unread for everyone else. Archived state is preserved
571
488
  - **Reinstall**: Acts as initial sync, so previously archived activities are unarchived (fresh start)
572
489
 
573
490
  ### Two-Way Sync: Avoiding Race Conditions
574
491
 
575
- When implementing two-way sync where items created in Plot are pushed to an external system (e.g. Notes becoming comments), a race condition can occur: the external system may send a webhook for the newly created item before you've updated the Activity/Note with the external key. The webhook handler won't find the item by external key and may create a duplicate.
492
+ When implementing two-way sync where items created in Plot are pushed to an external system (e.g. Notes becoming comments), a race condition can occur: the external system may send a webhook for the newly created item before you've updated the Thread/Note with the external key. The webhook handler won't find the item by external key and may create a duplicate.
576
493
 
577
- **Solution:** Embed the Plot `Activity.id` / `Note.id` in the external item's metadata when creating it, and update `Activity.source` / `Note.key` after creation. When processing webhooks, check for the Plot ID in metadata first.
494
+ **Solution:** Embed the Plot `Thread.id` / `Note.id` in the external item's metadata when creating it, and update `Thread.source` / `Note.key` after creation. When processing webhooks, check for the Plot ID in metadata first.
578
495
 
579
496
  ```typescript
580
497
  async pushNoteAsComment(note: Note, externalItemId: string): Promise<void> {
@@ -617,8 +534,8 @@ try {
617
534
  } catch (error) {
618
535
  console.error("Operation failed:", error);
619
536
 
620
- await this.tools.plot.createActivity({
621
- type: ActivityType.Note,
537
+ await this.tools.plot.createThread({
538
+ type: ThreadType.Note,
622
539
  title: "Operation failed",
623
540
  notes: [
624
541
  {
@@ -632,11 +549,11 @@ try {
632
549
  ## Common Pitfalls
633
550
 
634
551
  - **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.
635
- - **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.
636
- - **Always create Activities with Notes** - See "Understanding Activities and Notes" section above for the thread/message pattern and decision tree.
637
- - **Use correct Activity types** - Most should be `ActivityType.Note`. Only use `Action` for tasks with `done`, and `Event` for items with `start`/`end`.
638
- - **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).
639
- - **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.
552
+ - **Processing self-created threads** - Other users may change a Thread created by the twist, resulting in a callback. Be sure to check the `changes === null` and/or `thread.author.id !== this.id` to avoid re-processing.
553
+ - **Always create Threads with Notes** - See "Understanding Threads and Notes" section above for the thread/message pattern and decision tree.
554
+ - **Use correct Thread types** - Most should be `ThreadType.Note`. Only use `Action` for tasks with `done`, and `Event` for items with `start`/`end`.
555
+ - **Use Thread.source and Note.key for automatic upserts (Recommended)** - Set Thread.source to the external item's URL for automatic deduplication. Only use UUID generation and storage for advanced cases (see SYNC_STRATEGIES.md).
556
+ - **Add Notes to existing Threads** - For source/key pattern, reference threads by source. For UUID pattern, look up stored mappings before creating new Threads. Think thread replies, not new threads.
640
557
  - Tools are declared in the `build` method and accessed via `this.tools.toolName` in twist methods.
641
558
  - **Don't forget request limits** - Each execution has ~1000 requests (HTTP requests, tool calls). Break long loops into batches with `this.runTask()` to get fresh request limits. Calculate requests per item to determine safe batch size (e.g., if each item needs ~10 requests, batch size = ~100 items).
642
559
  - **Always use Callbacks tool for persistent references** - Direct function references don't survive worker restarts.