@plotday/twister 0.47.0 → 0.49.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 (123) hide show
  1. package/bin/commands/generate.js +5 -5
  2. package/bin/commands/generate.js.map +1 -1
  3. package/bin/templates/AGENTS.template.md +8 -2
  4. package/bin/utils/bundle.js +14 -0
  5. package/bin/utils/bundle.js.map +1 -1
  6. package/cli/templates/AGENTS.template.md +8 -2
  7. package/dist/connector.d.ts +67 -7
  8. package/dist/connector.d.ts.map +1 -1
  9. package/dist/connector.js +15 -5
  10. package/dist/connector.js.map +1 -1
  11. package/dist/docs/assets/hierarchy.js +1 -1
  12. package/dist/docs/assets/navigation.js +1 -1
  13. package/dist/docs/assets/search.js +1 -1
  14. package/dist/docs/classes/index.Connector.html +58 -49
  15. package/dist/docs/classes/index.Imap.html +1 -1
  16. package/dist/docs/classes/index.Options.html +1 -1
  17. package/dist/docs/classes/index.Smtp.html +1 -1
  18. package/dist/docs/classes/tools_ai.AI.html +1 -1
  19. package/dist/docs/classes/tools_callbacks.Callbacks.html +1 -1
  20. package/dist/docs/classes/tools_integrations.Integrations.html +21 -5
  21. package/dist/docs/classes/tools_network.Network.html +1 -1
  22. package/dist/docs/classes/tools_plot.Plot.html +1 -1
  23. package/dist/docs/classes/tools_store.Store.html +1 -1
  24. package/dist/docs/classes/tools_tasks.Tasks.html +1 -1
  25. package/dist/docs/classes/tools_twists.Twists.html +1 -1
  26. package/dist/docs/classes/twist.Twist.html +28 -28
  27. package/dist/docs/documents/Building_Connectors.html +8 -1
  28. package/dist/docs/documents/CLI_Reference.html +6 -4
  29. package/dist/docs/enums/tag.Tag.html +11 -1
  30. package/dist/docs/enums/tools_integrations.AuthProvider.html +14 -12
  31. package/dist/docs/hierarchy.html +1 -1
  32. package/dist/docs/media/AGENTS.md +298 -775
  33. package/dist/docs/media/MULTI_USER_AUTH.md +6 -4
  34. package/dist/docs/media/SYNC_STRATEGIES.md +20 -14
  35. package/dist/docs/modules/index.html +1 -1
  36. package/dist/docs/types/index.CreateLinkDraft.html +7 -12
  37. package/dist/docs/types/index.NoteWriteBackResult.html +38 -0
  38. package/dist/docs/types/tools_integrations.ArchiveLinkFilter.html +5 -5
  39. package/dist/docs/types/tools_integrations.AuthToken.html +4 -4
  40. package/dist/docs/types/tools_integrations.Authorization.html +4 -4
  41. package/dist/llm-docs/connector.d.ts +1 -1
  42. package/dist/llm-docs/connector.d.ts.map +1 -1
  43. package/dist/llm-docs/connector.js +1 -1
  44. package/dist/llm-docs/connector.js.map +1 -1
  45. package/dist/llm-docs/tag.d.ts +1 -1
  46. package/dist/llm-docs/tag.d.ts.map +1 -1
  47. package/dist/llm-docs/tag.js +1 -1
  48. package/dist/llm-docs/tag.js.map +1 -1
  49. package/dist/llm-docs/tools/integrations.d.ts +1 -1
  50. package/dist/llm-docs/tools/integrations.d.ts.map +1 -1
  51. package/dist/llm-docs/tools/integrations.js +1 -1
  52. package/dist/llm-docs/tools/integrations.js.map +1 -1
  53. package/dist/llm-docs/twist-guide-template.d.ts +1 -1
  54. package/dist/llm-docs/twist-guide-template.d.ts.map +1 -1
  55. package/dist/llm-docs/twist-guide-template.js +1 -1
  56. package/dist/llm-docs/twist-guide-template.js.map +1 -1
  57. package/dist/llm-docs/twist.d.ts +1 -1
  58. package/dist/llm-docs/twist.d.ts.map +1 -1
  59. package/dist/llm-docs/twist.js +1 -1
  60. package/dist/llm-docs/twist.js.map +1 -1
  61. package/dist/tag.d.ts +11 -1
  62. package/dist/tag.d.ts.map +1 -1
  63. package/dist/tag.js +10 -0
  64. package/dist/tag.js.map +1 -1
  65. package/dist/tools/integrations.d.ts +25 -1
  66. package/dist/tools/integrations.d.ts.map +1 -1
  67. package/dist/tools/integrations.js +2 -0
  68. package/dist/tools/integrations.js.map +1 -1
  69. package/dist/twist-guide.d.ts +1 -1
  70. package/dist/twist-guide.d.ts.map +1 -1
  71. package/dist/twist.d.ts +2 -1
  72. package/dist/twist.d.ts.map +1 -1
  73. package/dist/twist.js.map +1 -1
  74. package/dist/utils/markdown.d.ts +27 -0
  75. package/dist/utils/markdown.d.ts.map +1 -0
  76. package/dist/utils/markdown.js +82 -0
  77. package/dist/utils/markdown.js.map +1 -0
  78. package/package.json +7 -1
  79. package/src/connector.ts +427 -0
  80. package/src/creator-docs.ts +29 -0
  81. package/src/index.ts +10 -0
  82. package/src/llm-docs/connector.ts +8 -0
  83. package/src/llm-docs/index.ts +48 -0
  84. package/src/llm-docs/options.ts +8 -0
  85. package/src/llm-docs/plot.ts +8 -0
  86. package/src/llm-docs/schedule.ts +8 -0
  87. package/src/llm-docs/tag.ts +8 -0
  88. package/src/llm-docs/tool.ts +8 -0
  89. package/src/llm-docs/tools/ai.ts +8 -0
  90. package/src/llm-docs/tools/callbacks.ts +8 -0
  91. package/src/llm-docs/tools/imap.ts +8 -0
  92. package/src/llm-docs/tools/integrations.ts +8 -0
  93. package/src/llm-docs/tools/network.ts +8 -0
  94. package/src/llm-docs/tools/plot.ts +8 -0
  95. package/src/llm-docs/tools/smtp.ts +8 -0
  96. package/src/llm-docs/tools/store.ts +8 -0
  97. package/src/llm-docs/tools/tasks.ts +8 -0
  98. package/src/llm-docs/tools/twists.ts +8 -0
  99. package/src/llm-docs/twist-guide-template.ts +8 -0
  100. package/src/llm-docs/twist.ts +8 -0
  101. package/src/options.ts +115 -0
  102. package/src/plot.ts +1068 -0
  103. package/src/schedule.ts +203 -0
  104. package/src/tag.ts +54 -0
  105. package/src/tool.ts +377 -0
  106. package/src/tools/ai.ts +845 -0
  107. package/src/tools/callbacks.ts +134 -0
  108. package/src/tools/imap.ts +266 -0
  109. package/src/tools/index.ts +10 -0
  110. package/src/tools/integrations.ts +352 -0
  111. package/src/tools/network.ts +240 -0
  112. package/src/tools/plot.ts +692 -0
  113. package/src/tools/smtp.ts +166 -0
  114. package/src/tools/store.ts +149 -0
  115. package/src/tools/tasks.ts +137 -0
  116. package/src/tools/twists.ts +228 -0
  117. package/src/twist-guide.ts +9 -0
  118. package/src/twist.ts +436 -0
  119. package/src/utils/hash.ts +8 -0
  120. package/src/utils/markdown.ts +94 -0
  121. package/src/utils/serializable.ts +54 -0
  122. package/src/utils/types.ts +130 -0
  123. package/src/utils/uuid.ts +9 -0
package/src/plot.ts ADDED
@@ -0,0 +1,1068 @@
1
+ import type { NewSchedule, NewScheduleOccurrence, Schedule } from "./schedule";
2
+ import { type Tag } from "./tag";
3
+ import { type Callback } from "./tools/callbacks";
4
+ import { type AuthProvider } from "./tools/integrations";
5
+ import { type JSONValue } from "./utils/types";
6
+ import { Uuid } from "./utils/uuid";
7
+
8
+ export { Tag } from "./tag";
9
+ export { Uuid } from "./utils/uuid";
10
+ export { type JSONValue } from "./utils/types";
11
+ export { type AuthProvider } from "./tools/integrations";
12
+
13
+ /**
14
+ * @fileoverview
15
+ * Core Plot entity types for working with threads, notes, priorities, and contacts.
16
+ *
17
+ * ## Type Pattern: Null vs Undefined Semantics
18
+ *
19
+ * Plot entity types use a consistent pattern to distinguish between missing, unset, and explicitly cleared values:
20
+ *
21
+ * ### Entity Types (Thread, Priority, Note, Actor)
22
+ * - **Required fields**: No `?`, cannot be `undefined`
23
+ * - Example: `id: Uuid`, `title: string`
24
+ * - **Nullable fields**: Use `| null` to allow explicit clearing
25
+ * - Example: `assignee: ActorId | null`, `done: Date | null`
26
+ * - `null` = field is explicitly unset/cleared
27
+ * - Non-null value = field has a value
28
+ * - **Optional nullable fields**: Use `?` with `| null` for permission-based access
29
+ * - Example: `email?: string | null`, `name?: string | null`
30
+ * - `undefined` = field not included (e.g., no permission to access)
31
+ * - `null` = field included but not set
32
+ * - Value = field has a value
33
+ *
34
+ * ### New* Types (NewThread, NewNote, NewPriority)
35
+ * Used for creating or updating entities. Support partial updates by distinguishing omitted vs cleared fields:
36
+ * - **Required fields**: Must be provided (no `?`)
37
+ * - Example: `title: string` in NewPriority
38
+ * - **Optional fields**: Use `?` to make them optional
39
+ * - Example: `title?: string`, `author?: NewActor`
40
+ * - `undefined` (omitted) = don't set/update this field
41
+ * - Provided value = set/update this field
42
+ * - **Optional nullable fields**: Use `?` with `| null` to support clearing
43
+ * - Example: `assignee?: NewActor | null`
44
+ * - `undefined` (omitted) = don't change assignee
45
+ * - `null` = clear the assignee
46
+ * - NewActor = set/update the assignee
47
+ *
48
+ * This pattern allows API consumers to:
49
+ * 1. Omit fields they don't want to change (undefined)
50
+ * 2. Explicitly clear fields by setting to null
51
+ * 3. Set or update fields by providing values
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * // Creating a new thread
56
+ * const newThread: NewThread = {
57
+ * title: "Review pull request",
58
+ * };
59
+ *
60
+ * // Updating a thread - only change what's specified
61
+ * const update: ThreadUpdate = {
62
+ * id: threadId,
63
+ * archived: true,
64
+ * };
65
+ * ```
66
+ */
67
+
68
+ /**
69
+ * Represents a unique user, contact, or twist in Plot.
70
+ *
71
+ * ActorIds are used throughout Plot for:
72
+ * - Activity authors and assignees
73
+ * - Tag creators (actor_id in activity_tag/note_tag)
74
+ * - Mentions in activities and notes
75
+ * - Any entity that can perform actions in Plot
76
+ */
77
+ export type ActorId = string & { readonly __brand: "ActorId" };
78
+
79
+ /**
80
+ * Theme colors for priorities.
81
+ */
82
+ export enum ThemeColor {
83
+ /** Catalyst - Green */
84
+ Catalyst = 0,
85
+ /** Call to Adventure - Blue */
86
+ CallToAdventure = 1,
87
+ /** Rising Action - Purple */
88
+ RisingAction = 2,
89
+ /** Momentum - Pink-Purple */
90
+ Momentum = 3,
91
+ /** Turning Point - Pink */
92
+ TurningPoint = 4,
93
+ /** Breakthrough - Orange */
94
+ Breakthrough = 5,
95
+ /** Climax - Olive */
96
+ Climax = 6,
97
+ /** Resolution - Blue-Gray */
98
+ Resolution = 7,
99
+ }
100
+
101
+ /**
102
+ * Represents a priority context within Plot.
103
+ *
104
+ * Priorities are similar to projects in other apps. All Activity is in a Priority.
105
+ * Priorities can be nested.
106
+ */
107
+ export type Priority = {
108
+ /** Unique identifier for the priority */
109
+ id: Uuid;
110
+ /** Human-readable title for the priority */
111
+ title: string;
112
+ /** Whether this priority has been archived */
113
+ archived: boolean;
114
+ /**
115
+ * Optional key for referencing this priority.
116
+ * Keys are unique per priority tree (a user's personal priorities or the root of a shared priority).
117
+ */
118
+ key: string | null;
119
+ /** Optional theme color for the priority (0-7). If not set, inherits from parent or defaults to 7 (Resolution). */
120
+ color: ThemeColor | null;
121
+ };
122
+
123
+ /**
124
+ * Type for creating new priorities.
125
+ *
126
+ * Supports multiple creation patterns:
127
+ * - Provide a specific UUID for the priority
128
+ * - Provide a key for upsert within the user's priorities
129
+ * - Omit both to auto-generate a new UUID
130
+ *
131
+ * Optionally specify a parent priority by ID or key for hierarchical structures.
132
+ */
133
+ export type NewPriority = Pick<Priority, "title"> &
134
+ Partial<Omit<Priority, "id" | "title">> &
135
+ (
136
+ | {
137
+ /**
138
+ * Unique identifier for the priority, generated by Uuid.Generate().
139
+ * Specifying an ID allows tools to track and upsert priorities.
140
+ */
141
+ id: Uuid;
142
+ }
143
+ | {
144
+ /**
145
+ * Unique key for the priority within the user's priorities.
146
+ * Can be used to upsert without knowing the UUID.
147
+ * For example, "@plot" identifies the Plot priority.
148
+ */
149
+ key: string;
150
+ }
151
+ | {
152
+ /* Neither id nor key is required. An id will be generated and returned. */
153
+ }
154
+ ) & {
155
+ /** Add the new priority as the child of another priority */
156
+ parent?: { id: Uuid } | { key: string };
157
+ };
158
+
159
+ /**
160
+ * Type for updating existing priorities.
161
+ * Must provide either id or key to identify the priority to update.
162
+ * Set `parent` to move the priority under a new parent (requires PriorityAccess.Full).
163
+ */
164
+ export type PriorityUpdate = ({ id: Uuid } | { key: string }) &
165
+ Partial<Pick<Priority, "title" | "archived">> & {
166
+ /** Move the priority under a new parent. Requires PriorityAccess.Full. */
167
+ parent?: { id: Uuid } | { key: string };
168
+ };
169
+
170
+ /**
171
+ * Enumeration of supported action types.
172
+ *
173
+ * Different action types have different behaviors when clicked by users
174
+ * and may require different rendering approaches.
175
+ */
176
+ export enum ActionType {
177
+ /** External web links that open in browser */
178
+ external = "external",
179
+ /** Authentication flows for connecting services */
180
+ auth = "auth",
181
+ /** Callback actions that trigger twist methods when clicked */
182
+ callback = "callback",
183
+ /** Video conferencing links with provider-specific handling */
184
+ conferencing = "conferencing",
185
+ /** File attachment links stored in R2 */
186
+ file = "file",
187
+ /** Thread reference links for navigating to related threads */
188
+ thread = "thread",
189
+ /** Structured plan of operations for user approval */
190
+ plan = "plan",
191
+ }
192
+
193
+ /**
194
+ * Video conferencing providers for conferencing links.
195
+ *
196
+ * Used to identify the conferencing platform and provide
197
+ * provider-specific UI elements (titles, icons, etc.).
198
+ */
199
+ export enum ConferencingProvider {
200
+ /** Google Meet */
201
+ googleMeet = "googleMeet",
202
+ /** Zoom */
203
+ zoom = "zoom",
204
+ /** Microsoft Teams */
205
+ microsoftTeams = "microsoftTeams",
206
+ /** Cisco Webex */
207
+ webex = "webex",
208
+ /** Other or unknown conferencing provider */
209
+ other = "other",
210
+ }
211
+
212
+ /**
213
+ * Represents a clickable action attached to a thread.
214
+ *
215
+ * Thread actions are rendered as buttons that enable user interaction with threads.
216
+ * Different action types have specific behaviors and required fields for proper functionality.
217
+ *
218
+ * @example
219
+ * ```typescript
220
+ * // External action - opens URL in browser
221
+ * const externalAction: Action = {
222
+ * type: ActionType.external,
223
+ * title: "Open in Google Calendar",
224
+ * url: "https://calendar.google.com/event/123",
225
+ * };
226
+ *
227
+ * // Conferencing action - opens video conference with provider info
228
+ * const conferencingAction: Action = {
229
+ * type: ActionType.conferencing,
230
+ * url: "https://meet.google.com/abc-defg-hij",
231
+ * provider: ConferencingProvider.googleMeet,
232
+ * };
233
+ *
234
+ * // Integrations action - initiates OAuth flow
235
+ * const authAction: Action = {
236
+ * type: ActionType.auth,
237
+ * title: "Continue with Google",
238
+ * provider: AuthProvider.Google,
239
+ * scopes: ["https://www.googleapis.com/auth/calendar.readonly"],
240
+ * callback: "callback-token-for-auth-completion"
241
+ * };
242
+ *
243
+ * // Callback action - triggers a twist method
244
+ * const callbackAction: Action = {
245
+ * type: ActionType.callback,
246
+ * title: "📅 Primary Calendar",
247
+ * token: "callback-token-here"
248
+ * };
249
+ * ```
250
+ */
251
+ export type Action =
252
+ | {
253
+ /** External web link that opens in browser */
254
+ type: ActionType.external;
255
+ /** Display text for the action button */
256
+ title: string;
257
+ /** URL to open when clicked */
258
+ url: string;
259
+ }
260
+ | {
261
+ /** Video conferencing action with provider-specific handling */
262
+ type: ActionType.conferencing;
263
+ /** URL to join the conference */
264
+ url: string;
265
+ /** Conferencing provider for UI customization */
266
+ provider: ConferencingProvider;
267
+ }
268
+ | {
269
+ /** Authentication action that initiates an OAuth flow */
270
+ type: ActionType.auth;
271
+ /** Display text for the auth button */
272
+ title: string;
273
+ /** OAuth provider (e.g., "google", "microsoft") */
274
+ provider: string;
275
+ /** Array of OAuth scopes to request */
276
+ scopes: string[];
277
+ /** Callback token for auth completion notification */
278
+ callback: Callback;
279
+ }
280
+ | {
281
+ /** Callback action that triggers a twist method when clicked */
282
+ type: ActionType.callback;
283
+ /** Display text for the callback button */
284
+ title: string;
285
+ /** Token identifying the callback to execute */
286
+ callback: Callback;
287
+ }
288
+ | {
289
+ /** File attachment action stored in R2 */
290
+ type: ActionType.file;
291
+ /** Unique identifier for the stored file */
292
+ fileId: string;
293
+ /** Original filename */
294
+ fileName: string;
295
+ /** File size in bytes */
296
+ fileSize: number;
297
+ /** MIME type of the file */
298
+ mimeType: string;
299
+ /** Intrinsic width of the image in pixels (only for image files) */
300
+ imageWidth?: number | null;
301
+ /** Intrinsic height of the image in pixels (only for image files) */
302
+ imageHeight?: number | null;
303
+ }
304
+ | {
305
+ /** Thread reference action for navigating to a related thread */
306
+ type: ActionType.thread;
307
+ /** UUID of the referenced thread */
308
+ threadId: Uuid;
309
+ }
310
+ | {
311
+ /** Structured plan of operations for user approval */
312
+ type: ActionType.plan;
313
+ /** Human-readable summary of the plan */
314
+ title: string;
315
+ /** Operations to execute on approval */
316
+ operations: PlanOperation[];
317
+ /** Callback invoked with (action, approved: boolean) */
318
+ callback: Callback;
319
+ };
320
+
321
+ /**
322
+ * Represents metadata about a thread, typically from an external system.
323
+ *
324
+ * Thread metadata enables storing additional information about threads,
325
+ * which is useful for synchronization, linking back to external systems,
326
+ * and storing tool-specific data.
327
+ *
328
+ * Must be valid JSON data (strings, numbers, booleans, null, objects, arrays).
329
+ * Functions and other non-JSON values are not supported.
330
+ *
331
+ * @example
332
+ * ```typescript
333
+ * // Calendar event metadata
334
+ * await plot.createThread({
335
+ * title: "Team Meeting",
336
+ * meta: {
337
+ * calendarId: "primary",
338
+ * htmlLink: "https://calendar.google.com/event/abc123",
339
+ * conferenceData: { ... }
340
+ * }
341
+ * });
342
+ *
343
+ * // Project issue metadata
344
+ * await plot.createThread({
345
+ * title: "Fix login bug",
346
+ * meta: {
347
+ * projectId: "TEAM",
348
+ * issueNumber: 123,
349
+ * url: "https://linear.app/team/issue/TEAM-123"
350
+ * }
351
+ * });
352
+ * ```
353
+ */
354
+ export type ThreadMeta = {
355
+ /** Source-specific properties and metadata */
356
+ [key: string]: JSONValue;
357
+ };
358
+
359
+ /**
360
+ * Thread sub-type that determines the thread's icon and category.
361
+ * Available types depend on whether the priority is shared:
362
+ * - Private priorities: "action" (default for tasks), "notes" (default), "idea", "goal", "decision"
363
+ * - Shared priorities: all above plus "discussion" (default), "announcement", "ask"
364
+ */
365
+ export type ThreadType =
366
+ | "action"
367
+ | "notes"
368
+ | "idea"
369
+ | "goal"
370
+ | "decision"
371
+ | "discussion"
372
+ | "announcement"
373
+ | "ask";
374
+
375
+ /**
376
+ * Tags on an item, along with the actors who added each tag.
377
+ */
378
+ export type Tags = { [K in Tag]?: ActorId[] };
379
+
380
+ /**
381
+ * A set of tags to add to an item, along with the actors adding each tag.
382
+ */
383
+ export type NewTags = { [K in Tag]?: NewActor[] };
384
+
385
+ /**
386
+ * Thread access level determining visibility.
387
+ * - "public": Visible to all users with priority access
388
+ * - "members": Visible to priority members (default for shared priorities)
389
+ * - "private": Visible only to creator and contacts listed in accessContacts
390
+ */
391
+ export type ThreadAccessLevel = "public" | "members" | "private";
392
+
393
+ /**
394
+ * Common fields shared by both Thread and Note entities.
395
+ */
396
+ export type ThreadCommon = {
397
+ /** Unique identifier for the thread */
398
+ id: Uuid;
399
+ /**
400
+ * When this item was created.
401
+ *
402
+ * **For sources:** Set this to the external system's timestamp (e.g., email
403
+ * sent date, comment creation date), NOT the sync time. If omitted, defaults
404
+ * to the current time, which is almost never correct for synced data.
405
+ */
406
+ created: Date;
407
+ /** Whether this thread has been archived */
408
+ archived: boolean;
409
+ /** Tags attached to this thread. Maps tag ID to array of actor IDs who added that tag. */
410
+ tags: Tags;
411
+ };
412
+
413
+ /**
414
+ * Fields on a Thread entity.
415
+ * Threads are simple containers for links and notes.
416
+ */
417
+ type ThreadFields = ThreadCommon & {
418
+ /** The display title/summary of the thread */
419
+ title: string;
420
+ /** The priority context this thread belongs to */
421
+ priority: Priority;
422
+ /** The thread's sub-type/category. Determines the displayed icon. */
423
+ type: ThreadType | null;
424
+ /** Thread access level: "public", "members", or "private" */
425
+ access: ThreadAccessLevel;
426
+ /** Contacts who can see a private thread (empty array for creator-only). Only meaningful when access is "private". */
427
+ accessContacts: Contact[];
428
+ /** The schedule associated with this thread, if any */
429
+ schedule?: Schedule;
430
+ /** Source-specific metadata from the thread's link, populated on callbacks */
431
+ meta?: ThreadMeta;
432
+ };
433
+
434
+ export type Thread = ThreadFields;
435
+
436
+ export type ThreadWithNotes = Thread & {
437
+ notes: Note[];
438
+ };
439
+
440
+ export type NewThreadWithNotes = NewThread & {
441
+ notes: Omit<NewNote, "thread">[];
442
+ };
443
+
444
+ /**
445
+ * Type for creating new threads.
446
+ *
447
+ * Threads are simple containers. All other fields are optional.
448
+ *
449
+ * @example
450
+ * ```typescript
451
+ * const thread: NewThread = {
452
+ * title: "Review pull request"
453
+ * };
454
+ * ```
455
+ */
456
+ export type NewThread = Partial<
457
+ Omit<ThreadFields, "priority" | "tags" | "id" | "accessContacts">
458
+ > &
459
+ (
460
+ | {
461
+ /** Unique identifier for the thread, generated by Uuid.Generate(). */
462
+ id: Uuid;
463
+ }
464
+ | {
465
+ /* id is optional. An id will be generated and returned. */
466
+ }
467
+ ) &
468
+ {
469
+ /** Explicit priority - disables automatic priority matching. When omitted, the server classifies the thread using the user's priority rules. */
470
+ priority?: Pick<Priority, "id">;
471
+ } & {
472
+ /**
473
+ * All tags to set on the new thread.
474
+ */
475
+ tags?: NewTags;
476
+
477
+ /**
478
+ * The thread's sub-type/category. Sets the thread's icon.
479
+ * If omitted, defaults to "notes" (private) or "discussion" (shared).
480
+ */
481
+ type?: ThreadType;
482
+
483
+ /**
484
+ * Contacts who can see a private thread.
485
+ * Pass email-based NewContact objects; they are resolved to contact IDs by the API.
486
+ * If omitted for a private thread, defaults to the connection owner.
487
+ */
488
+ accessContacts?: NewContact[];
489
+
490
+ /**
491
+ * Whether the thread should be marked as unread for users.
492
+ * - undefined/omitted (default): Thread is unread for users, except auto-marked
493
+ * as read for the author if they are the twist owner (user)
494
+ * - true: Thread is explicitly unread for ALL users (use sparingly)
495
+ * - false: Thread is marked as read for all users in the priority at creation time
496
+ */
497
+ unread?: boolean;
498
+
499
+ /**
500
+ * Whether the thread is archived.
501
+ * - true: Archive the thread
502
+ * - false: Unarchive the thread
503
+ * - undefined (default): Preserve current archive state
504
+ */
505
+ archived?: boolean;
506
+
507
+ /**
508
+ * Optional preview content for the thread. Can be Markdown formatted.
509
+ * The preview will be automatically generated from this content (truncated to 100 chars).
510
+ */
511
+ preview?: string | null;
512
+
513
+ /**
514
+ * Optional schedules to create alongside the thread.
515
+ */
516
+ schedules?: Array<Omit<NewSchedule, "threadId">>;
517
+
518
+ /**
519
+ * Optional schedule occurrence overrides.
520
+ */
521
+ scheduleOccurrences?: NewScheduleOccurrence[];
522
+ };
523
+
524
+ export type ThreadFilter = {
525
+ meta?: {
526
+ [key: string]: JSONValue;
527
+ };
528
+ };
529
+
530
+ /**
531
+ * Fields supported by bulk updates via `match`. Only simple scalar fields
532
+ * that can be applied uniformly across many threads are included.
533
+ */
534
+ type ThreadBulkUpdateFields = Partial<
535
+ Pick<ThreadFields, "title" | "access" | "archived">
536
+ > & {
537
+ /** Contacts who can see a private thread. Pass NewContact objects (email-based); resolved by the API. */
538
+ accessContacts?: NewContact[];
539
+ };
540
+
541
+ /**
542
+ * Fields supported by single-thread updates via `id` or `source`.
543
+ * Includes all bulk fields plus tags and preview.
544
+ */
545
+ type ThreadSingleUpdateFields = ThreadBulkUpdateFields & {
546
+ /**
547
+ * Tags to change on the thread. Use an empty array of NewActor to remove a tag.
548
+ * Use twistTags to add/remove the twist from tags to avoid clearing other actors' tags.
549
+ */
550
+ tags?: NewTags;
551
+
552
+ /**
553
+ * Add or remove the twist's tags.
554
+ * Maps tag ID to boolean: true = add tag, false = remove tag.
555
+ * This is allowed on all threads the twist has access to.
556
+ */
557
+ twistTags?: Partial<Record<Tag, boolean>>;
558
+
559
+ /**
560
+ * Update the thread's sub-type/category.
561
+ */
562
+ type?: ThreadType;
563
+
564
+ /**
565
+ * Optional preview content for the thread. Can be Markdown formatted.
566
+ * The preview will be automatically generated from this content (truncated to 100 chars).
567
+ *
568
+ * - string: Use this content for preview generation
569
+ * - null: Explicitly disable preview (no preview will be shown)
570
+ * - undefined (omitted): Preserve current preview value
571
+ *
572
+ * This field is write-only and won't be returned when reading threads.
573
+ */
574
+ preview?: string | null;
575
+
576
+ /**
577
+ * Move the thread to a different priority. Requires ThreadAccess.Full.
578
+ * The target priority must be owned by the twist's user.
579
+ */
580
+ priority?: Pick<Priority, "id">;
581
+ };
582
+
583
+ export type ThreadUpdate =
584
+ | (({ id: Uuid } | { source: string }) & ThreadSingleUpdateFields)
585
+ | ({
586
+ /**
587
+ * Update all threads matching the specified criteria. Only threads
588
+ * that match all provided fields and were created by the twist will be updated.
589
+ */
590
+ match: ThreadFilter;
591
+ } & ThreadBulkUpdateFields);
592
+
593
+ /**
594
+ * Represents a note within a thread.
595
+ *
596
+ * Notes contain the detailed content (note text, actions) associated with a thread.
597
+ * They are always ordered by creation time within their parent thread.
598
+ */
599
+ export type Note = ThreadCommon & {
600
+ /** The author of this note */
601
+ author: Actor;
602
+ /**
603
+ * Globally unique, stable identifier for the note within its thread.
604
+ * Can be used to upsert without knowing the id.
605
+ *
606
+ * Use one of these patterns:
607
+ * - Hardcoded semantic keys for fixed note types: "description", "cancellation"
608
+ * - External service IDs for dynamic collections: `comment:${immutableId}`
609
+ *
610
+ * Examples:
611
+ * - `"description"` (for a Jira issue's description note)
612
+ * - `"comment:12345"` (for a specific comment by ID)
613
+ * - `"gmail:msg:18d4e5f2a3b1c9d7"` (for a Gmail message within a thread)
614
+ *
615
+ * Ensure IDs are immutable - avoid human-readable slugs or titles.
616
+ */
617
+ key: string | null;
618
+ /** The parent thread this note belongs to */
619
+ thread: Thread;
620
+ /** Primary content for the note (markdown) */
621
+ content: string | null;
622
+ /** Array of interactive actions attached to the note */
623
+ actions: Array<Action> | null;
624
+ /** The note this is a reply to, or null if not a reply */
625
+ reNote: { id: Uuid } | null;
626
+ /**
627
+ * Contacts who can see this note, or null if the note inherits thread visibility.
628
+ * When set (even to []), the note is private to the listed contacts plus the creator.
629
+ */
630
+ accessContacts: ActorId[] | null;
631
+ /** Priority twist IDs (twists/connectors) mentioned for dispatch routing. Does not include user contacts. */
632
+ mentions: ActorId[];
633
+ };
634
+
635
+ /**
636
+ * Type for creating new notes.
637
+ *
638
+ * Requires the thread reference, with all other fields optional.
639
+ * Can provide id, key, or neither for note identification:
640
+ * - id: Provide a specific UUID for the note
641
+ * - key: Provide an external identifier for upsert within the thread
642
+ * - neither: A new note with auto-generated UUID will be created
643
+ */
644
+ export type NewNote = Partial<
645
+ Omit<
646
+ Note,
647
+ "author" | "thread" | "tags" | "mentions" | "accessContacts" | "id" | "key" | "reNote"
648
+ >
649
+ > &
650
+ ({ id: Uuid } | { key: string } | {}) & {
651
+ /** Reference to the parent thread (required) */
652
+ thread:
653
+ | Pick<Thread, "id">
654
+ | {
655
+ source: string;
656
+ };
657
+
658
+ /**
659
+ * The person that created the item, or leave undefined to use the twist as author.
660
+ */
661
+ author?: NewActor;
662
+
663
+ /**
664
+ * Format of the note content. Determines how the note is processed:
665
+ * - 'text': Plain text that will be converted to markdown (auto-links URLs, preserves line breaks)
666
+ * - 'markdown': Already in markdown format (default, no conversion)
667
+ * - 'html': HTML content that will be converted to markdown
668
+ */
669
+ contentType?: ContentType;
670
+
671
+ /**
672
+ * Tags to change on the thread. Use an empty array of NewActor to remove a tag.
673
+ * Use twistTags to add/remove the twist from tags to avoid clearing other actors' tags.
674
+ */
675
+ tags?: NewTags;
676
+
677
+ /**
678
+ * Contacts who can see this note, or null/undefined to inherit thread visibility.
679
+ * Accepts resolved ActorId UUIDs or email-based NewContact objects (resolved server-side).
680
+ * Include all participants who should see the note (sender + recipients).
681
+ * The note author is NOT implicitly included — add them explicitly.
682
+ * When set (even to []), the note is private to the listed contacts plus the creator.
683
+ */
684
+ accessContacts?: (ActorId | NewContact)[] | null;
685
+
686
+ /**
687
+ * Twist/connector IDs to mention for dispatch routing.
688
+ * Does not include user contacts — use accessContacts for visibility.
689
+ */
690
+ mentions?: NewActor[];
691
+
692
+ /**
693
+ * Whether the note should mark the parent thread as unread for users.
694
+ * - undefined/omitted (default): Thread is unread for users, except auto-marked
695
+ * as read for the author if they are the twist owner (user)
696
+ * - true: Thread is explicitly unread for ALL users (use sparingly)
697
+ * - false: Thread is marked as read for all users in the priority at note creation time
698
+ *
699
+ * For the default behavior, omit this field entirely.
700
+ * Use false for initial sync to avoid marking historical items as unread.
701
+ */
702
+ unread?: boolean;
703
+
704
+ /**
705
+ * When true, the server will use AI to detect tasks in this note's content
706
+ * and create separate Plot-authored reply notes for each detected task.
707
+ * Use for messaging connectors (email, chat) where tasks are implicit
708
+ * in conversation rather than explicitly structured.
709
+ */
710
+ checkForTasks?: boolean;
711
+
712
+ /**
713
+ * Reference to a parent note this note is a reply to.
714
+ * - `{ id }`: reply by UUID
715
+ * - `{ key }`: reply by key, resolved after creation (for batch ops)
716
+ * - `null`: explicitly not a reply
717
+ * - `undefined` (omitted): not a reply
718
+ */
719
+ reNote?: { id: Uuid } | { key: string } | null;
720
+ };
721
+
722
+ /**
723
+ * Type for updating existing notes.
724
+ * Must provide either id or key to identify the note to update.
725
+ */
726
+ export type NoteUpdate = ({ id: Uuid; key?: string } | { key: string }) &
727
+ Partial<
728
+ Pick<Note, "accessContacts" | "archived" | "content" | "actions" | "reNote">
729
+ > & {
730
+ /**
731
+ * Format of the note content. Determines how the note is processed:
732
+ * - 'text': Plain text that will be converted to markdown (auto-links URLs, preserves line breaks)
733
+ * - 'markdown': Already in markdown format (default, no conversion)
734
+ * - 'html': HTML content that will be converted to markdown
735
+ */
736
+ contentType?: ContentType;
737
+
738
+ /**
739
+ * Tags to change on the note. Use an empty array of NewActor to remove a tag.
740
+ * Use twistTags to add/remove the twist from tags to avoid clearing other actors' tags.
741
+ */
742
+ tags?: NewTags;
743
+
744
+ /**
745
+ * Add or remove the twist's tags.
746
+ * Maps tag ID to boolean: true = add tag, false = remove tag.
747
+ * This is allowed on all notes the twist has access to.
748
+ */
749
+ twistTags?: Partial<Record<Tag, boolean>>;
750
+
751
+ /**
752
+ * Twist/connector IDs to mention for dispatch routing.
753
+ * Does not include user contacts — use accessContacts for visibility.
754
+ */
755
+ mentions?: NewActor[];
756
+ };
757
+
758
+ /**
759
+ * Represents an actor in Plot - a user, contact, or twist.
760
+ *
761
+ * Actors can be associated with threads as authors, assignees, or mentions.
762
+ * The email field is only included when ContactAccess.Read permission is granted.
763
+ *
764
+ * @example
765
+ * ```typescript
766
+ * const actor: Actor = {
767
+ * id: "f0ffd5f8-1635-4b13-9532-35f97446db90" as ActorId,
768
+ * type: ActorType.Contact,
769
+ * email: "john.doe@example.com", // Only if ContactAccess.Read
770
+ * name: "John Doe"
771
+ * };
772
+ * ```
773
+ */
774
+ export type Actor = {
775
+ /** Unique identifier for the actor */
776
+ id: ActorId;
777
+ /** Type of actor (User, Contact, or Twist) */
778
+ type: ActorType;
779
+ /**
780
+ * Email address (only included with ContactAccess.Read permission).
781
+ * - `undefined`: No permission to read email
782
+ * - `null`: Permission granted but email not set
783
+ * - `string`: Email address
784
+ */
785
+ email?: string | null;
786
+ /**
787
+ * Display name.
788
+ * - `undefined`: Not included due to permissions
789
+ * - `null`: Not set
790
+ * - `string`: Display name
791
+ */
792
+ name?: string | null;
793
+ };
794
+
795
+ /**
796
+ * A resolved contact with identity info. Used for access control lists
797
+ * where only human contacts (not twists) are relevant.
798
+ */
799
+ export type Contact = {
800
+ /** Unique identifier for the contact */
801
+ id: ActorId;
802
+ /** Email address, or null if not set */
803
+ email: string | null;
804
+ /** Display name, or null if not set */
805
+ name: string | null;
806
+ };
807
+
808
+ /**
809
+ * An existing or new contact.
810
+ */
811
+ export type NewActor =
812
+ | {
813
+ /** Unique identifier for the actor */
814
+ id: ActorId;
815
+ }
816
+ | NewContact;
817
+
818
+ /**
819
+ * Enumeration of author types that can create threads.
820
+ *
821
+ * The author type affects how threads are displayed and processed
822
+ * within the Plot system.
823
+ */
824
+ export enum ActorType {
825
+ /** Threads created by human users */
826
+ User,
827
+ /** Threads created by external contacts */
828
+ Contact,
829
+ /** Threads created by automated twists */
830
+ Twist,
831
+ }
832
+
833
+ /**
834
+ * Represents contact information for creating a new contact.
835
+ *
836
+ * Contacts are used throughout Plot for representing people associated
837
+ * with activities, such as event attendees or task assignees.
838
+ *
839
+ * @example
840
+ * ```typescript
841
+ * const newContact: NewContact = {
842
+ * email: "john.doe@example.com",
843
+ * name: "John Doe",
844
+ * avatar: "https://avatar.example.com/john.jpg"
845
+ * };
846
+ * ```
847
+ */
848
+ /**
849
+ * Common fields shared by all NewContact variants.
850
+ */
851
+ type NewContactBase = {
852
+ /** Optional avatar image URL for the contact */
853
+ avatar?: string;
854
+ /**
855
+ * External provider account source. Used for identity resolution
856
+ * when email is unavailable and for privacy compliance reporting.
857
+ */
858
+ source?: { provider: AuthProvider; accountId: string };
859
+ };
860
+
861
+ /**
862
+ * At least one of `email` or `name` must be provided so the contact can be
863
+ * identified in the UI. Contacts with neither would display as "Unknown".
864
+ */
865
+ export type NewContact = NewContactBase &
866
+ ({ email: string; name?: string } | { email?: string; name: string });
867
+
868
+ export type ContentType = "text" | "markdown" | "html";
869
+
870
+ /**
871
+ * Represents an external entity linked to a thread.
872
+ *
873
+ * Links are created by sources to represent external entities (issues, emails, calendar events)
874
+ * attached to a thread container. A thread can have multiple links (1:many).
875
+ * Links store source-specific data like type, status, metadata, and embeddings.
876
+ *
877
+ * @example
878
+ * ```typescript
879
+ * // A link representing a Linear issue
880
+ * const link: Link = {
881
+ * threadId: "..." as Uuid,
882
+ * source: "linear:issue:549dd8bd-2bc9-43d1-95d5-4b4af0c5af1b",
883
+ * created: new Date(),
884
+ * author: { id: "..." as ActorId, type: ActorType.Contact, name: "Alice" },
885
+ * title: "Fix login bug",
886
+ * type: "issue",
887
+ * status: "open",
888
+ * meta: { projectId: "TEAM", url: "https://linear.app/team/TEAM-123" },
889
+ * assignee: null,
890
+ * actions: null,
891
+ * };
892
+ * ```
893
+ */
894
+ export type Link = {
895
+ /** The thread this link belongs to */
896
+ threadId: Uuid;
897
+ /** External source identifier for dedup/upsert */
898
+ source: string | null;
899
+ /** When this link was originally created in its source system */
900
+ created: Date;
901
+ /** The actor credited with creating this link */
902
+ author: Actor | null;
903
+ /** Display title */
904
+ title: string;
905
+ /** Truncated preview */
906
+ preview: string | null;
907
+ /** The actor assigned to this link */
908
+ assignee: Actor | null;
909
+ /** Source-defined type string (e.g., issue, pull_request, email, event) */
910
+ type: string | null;
911
+ /** Source-defined status string (e.g., open, done, closed) */
912
+ status: string | null;
913
+ /** Interactive action buttons */
914
+ actions: Array<Action> | null;
915
+ /** Source metadata */
916
+ meta: ThreadMeta | null;
917
+ /** URL to open the original item in its source application (e.g., "Open in Linear") */
918
+ sourceUrl: string | null;
919
+ /** Channel ID that produced this link (matches source_channel.channel_id) */
920
+ channelId: string | null;
921
+ /**
922
+ * Cross-connector thread bundling key.
923
+ * When set, this link shares a thread with any link whose `source` matches
924
+ * this value (or whose `relatedSource` matches this link's `source`).
925
+ * Works regardless of creation order.
926
+ */
927
+ relatedSource: string | null;
928
+ };
929
+
930
+ /**
931
+ * Type for creating new links.
932
+ *
933
+ * Links are created by sources to represent external entities.
934
+ * Requires a source identifier for dedup/upsert.
935
+ */
936
+ export type NewLink = (
937
+ | {
938
+ /**
939
+ * Canonical ID for the item in an external system.
940
+ * When set, uniquely identifies the link within a priority tree. This performs
941
+ * an upsert.
942
+ */
943
+ source: string;
944
+ }
945
+ | {}
946
+ ) &
947
+ Partial<Omit<Link, "source" | "author" | "assignee" | "threadId">> & {
948
+ /** The person that created the item. By default, it will be the twist itself. */
949
+ author?: NewActor;
950
+ /** The person assigned to the item. */
951
+ assignee?: NewActor | null;
952
+ /**
953
+ * Thread access level: "public", "members" (default), or "private".
954
+ * When "private", thread visibility is limited to the creator and contacts in accessContacts.
955
+ */
956
+ access?: ThreadAccessLevel;
957
+ /**
958
+ * Contacts who can see a private thread.
959
+ * Pass email-based NewContact objects; they are resolved to contact IDs by the API.
960
+ * If omitted for a private thread, defaults to the connection owner.
961
+ */
962
+ accessContacts?: NewContact[];
963
+ /**
964
+ * Whether the thread should be marked as unread for users.
965
+ * - undefined/omitted (default): Thread is unread for users, except auto-marked
966
+ * as read for the author if they are the twist owner (user)
967
+ * - false: Thread is marked as read for all users in the priority at creation time
968
+ */
969
+ unread?: boolean;
970
+ /**
971
+ * Whether the thread is archived.
972
+ * - true: Archive the thread
973
+ * - false: Unarchive the thread
974
+ * - undefined (default): Preserve current archive state
975
+ */
976
+ archived?: boolean;
977
+ /**
978
+ * Explicit priority (disables automatic priority matching).
979
+ * Only used when the link creates a new thread. When omitted, the
980
+ * server classifies the thread using the user's priority rules.
981
+ */
982
+ priority?: Pick<Priority, "id">;
983
+ };
984
+
985
+ /**
986
+ * A new link with notes to save via integrations.saveLink().
987
+ * Creates a thread+link pair, with notes attached to the thread.
988
+ */
989
+ export type NewLinkWithNotes = NewLink & {
990
+ /**
991
+ * Title for the link and its thread container.
992
+ * Must be the real entity title (e.g. issue title, message subject),
993
+ * never a placeholder or ID. This value overwrites the existing title on upsert.
994
+ * Omit to preserve the existing title (e.g. for cancelled events where the
995
+ * title may not be available in the webhook payload).
996
+ */
997
+ title?: string;
998
+ /** Notes to attach to the thread */
999
+ notes?: Omit<NewNote, "thread">[];
1000
+ /** Schedules to create for the link */
1001
+ schedules?: Array<Omit<NewSchedule, "threadId">>;
1002
+ /** Schedule occurrence overrides */
1003
+ scheduleOccurrences?: NewScheduleOccurrence[];
1004
+ };
1005
+
1006
+ /**
1007
+ * Type for updating existing links.
1008
+ *
1009
+ * Set `threadId` to move the link to a different thread.
1010
+ * Requires LinkAccess.Full.
1011
+ */
1012
+ export type LinkUpdate = { id: Uuid } & {
1013
+ /** Move the link to a different thread owned by the twist's user. */
1014
+ threadId?: Uuid;
1015
+ };
1016
+
1017
+ /**
1018
+ * A single operation within a plan submitted for user approval.
1019
+ *
1020
+ * Operations include display metadata (titles) so the app can render
1021
+ * a human-readable summary without additional lookups.
1022
+ */
1023
+ export type PlanOperation =
1024
+ | {
1025
+ type: "updateThread";
1026
+ threadId: Uuid;
1027
+ /** Current thread title for display */
1028
+ threadTitle: string;
1029
+ changes: Partial<Pick<ThreadFields, "archived" | "title" | "type">> & {
1030
+ /** Move to this priority */
1031
+ priority?: { id: Uuid; title: string };
1032
+ };
1033
+ }
1034
+ | {
1035
+ type: "updateLink";
1036
+ linkId: Uuid;
1037
+ /** Current link title for display */
1038
+ linkTitle: string;
1039
+ changes: {
1040
+ /** Move to this thread */
1041
+ threadId?: Uuid;
1042
+ threadTitle?: string;
1043
+ };
1044
+ }
1045
+ | {
1046
+ type: "createThread";
1047
+ title: string;
1048
+ priorityId: Uuid;
1049
+ /** Priority title for display */
1050
+ priorityTitle: string;
1051
+ }
1052
+ | {
1053
+ type: "createNote";
1054
+ threadId: Uuid;
1055
+ /** Thread title for display */
1056
+ threadTitle: string;
1057
+ content: string;
1058
+ }
1059
+ | {
1060
+ type: "updatePriority";
1061
+ priorityId: Uuid;
1062
+ /** Current priority title for display */
1063
+ priorityTitle: string;
1064
+ changes: Partial<Pick<Priority, "title" | "archived">> & {
1065
+ /** Move under this parent */
1066
+ parent?: { id: Uuid; title: string };
1067
+ };
1068
+ };