@plotday/twister 0.53.0 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/connector.d.ts +19 -4
- package/dist/connector.d.ts.map +1 -1
- package/dist/connector.js.map +1 -1
- package/dist/docs/assets/hierarchy.js +1 -1
- package/dist/docs/assets/navigation.js +1 -1
- package/dist/docs/assets/search.js +1 -1
- package/dist/docs/classes/index.Connector.html +25 -25
- package/dist/docs/classes/index.FileNotFoundError.html +1 -1
- package/dist/docs/classes/index.Files.html +2 -2
- package/dist/docs/classes/index.Imap.html +1 -1
- package/dist/docs/classes/index.Options.html +1 -1
- package/dist/docs/classes/index.Smtp.html +1 -1
- package/dist/docs/classes/tools_ai.AI.html +7 -5
- package/dist/docs/classes/tools_callbacks.Callbacks.html +1 -1
- package/dist/docs/classes/tools_integrations.Integrations.html +11 -11
- package/dist/docs/classes/tools_network.Network.html +1 -1
- package/dist/docs/classes/tools_plot.Plot.html +62 -56
- package/dist/docs/classes/tools_store.Store.html +1 -1
- package/dist/docs/classes/tools_tasks.Tasks.html +1 -1
- package/dist/docs/classes/tools_twists.Twists.html +2 -2
- package/dist/docs/classes/twist.Twist.html +3 -3
- package/dist/docs/enums/plot.ActionType.html +9 -9
- package/dist/docs/enums/plot.ActorType.html +4 -4
- package/dist/docs/enums/plot.ConferencingProvider.html +6 -6
- package/dist/docs/enums/plot.ThemeColor.html +1 -1
- package/dist/docs/enums/tools_ai.AIModel.html +2 -2
- package/dist/docs/enums/tools_integrations.AuthProvider.html +13 -13
- package/dist/docs/enums/{tools_plot.PriorityAccess.html → tools_plot.FocusAccess.html} +6 -6
- package/dist/docs/enums/tools_plot.ThreadAccess.html +1 -1
- package/dist/docs/hierarchy.html +1 -1
- package/dist/docs/interfaces/tools_ai.AIRequest.html +27 -11
- package/dist/docs/interfaces/tools_ai.AIResponse.html +9 -9
- package/dist/docs/interfaces/tools_ai.FilePart.html +5 -5
- package/dist/docs/interfaces/tools_ai.ImagePart.html +4 -4
- package/dist/docs/interfaces/tools_ai.ReasoningPart.html +4 -4
- package/dist/docs/interfaces/tools_ai.RedactedReasoningPart.html +3 -3
- package/dist/docs/interfaces/tools_ai.TextPart.html +3 -3
- package/dist/docs/interfaces/tools_ai.ToolCallPart.html +5 -5
- package/dist/docs/interfaces/tools_ai.ToolExecutionOptions.html +4 -4
- package/dist/docs/interfaces/tools_ai.ToolResultPart.html +5 -5
- package/dist/docs/media/AGENTS.md +1 -1
- package/dist/docs/modules/index.html +1 -1
- package/dist/docs/modules/plot.html +1 -1
- package/dist/docs/modules/tools_plot.html +1 -1
- package/dist/docs/types/index.CreateLinkDraft.html +14 -13
- package/dist/docs/types/index.ResolvedRecipient.html +13 -2
- package/dist/docs/types/index.Schedule.html +1 -1
- package/dist/docs/types/plot.Action.html +1 -1
- package/dist/docs/types/plot.Actor.html +5 -5
- package/dist/docs/types/plot.Contact.html +4 -4
- package/dist/docs/types/plot.ContentType.html +1 -1
- package/dist/docs/types/plot.Focus.html +18 -0
- package/dist/docs/types/plot.FocusUpdate.html +3 -0
- package/dist/docs/types/plot.Link.html +16 -16
- package/dist/docs/types/plot.LinkUpdate.html +1 -1
- package/dist/docs/types/plot.NewActor.html +1 -1
- package/dist/docs/types/plot.NewContact.html +1 -1
- package/dist/docs/types/plot.NewFocus.html +13 -0
- package/dist/docs/types/plot.NewLink.html +6 -6
- package/dist/docs/types/plot.NewLinkWithNotes.html +1 -1
- package/dist/docs/types/plot.NewNote.html +2 -2
- package/dist/docs/types/plot.NewReactions.html +1 -1
- package/dist/docs/types/plot.NewTags.html +1 -1
- package/dist/docs/types/plot.NewThread.html +4 -4
- package/dist/docs/types/plot.NewThreadWithNotes.html +1 -1
- package/dist/docs/types/plot.Note.html +2 -2
- package/dist/docs/types/plot.NoteUpdate.html +1 -1
- package/dist/docs/types/plot.PlanOperation.html +6 -6
- package/dist/docs/types/plot.Reaction.html +1 -1
- package/dist/docs/types/plot.Reactions.html +1 -1
- package/dist/docs/types/plot.Tags.html +1 -1
- package/dist/docs/types/plot.Thread.html +1 -1
- package/dist/docs/types/plot.ThreadAccessLevel.html +3 -3
- package/dist/docs/types/plot.ThreadCommon.html +6 -6
- package/dist/docs/types/plot.ThreadFilter.html +2 -2
- package/dist/docs/types/plot.ThreadMeta.html +1 -1
- package/dist/docs/types/plot.ThreadType.html +4 -4
- package/dist/docs/types/plot.ThreadUpdate.html +1 -1
- package/dist/docs/types/plot.ThreadWithNotes.html +1 -1
- package/dist/docs/types/tools_ai.AIAssistantMessage.html +2 -2
- package/dist/docs/types/tools_ai.AICapabilities.html +7 -3
- package/dist/docs/types/tools_ai.AIMessage.html +1 -1
- package/dist/docs/types/tools_ai.AIOptions.html +2 -2
- package/dist/docs/types/tools_ai.AISource.html +1 -1
- package/dist/docs/types/tools_ai.AISystemMessage.html +2 -2
- package/dist/docs/types/tools_ai.AITool.html +10 -9
- package/dist/docs/types/tools_ai.AIToolMessage.html +2 -2
- package/dist/docs/types/tools_ai.AIToolSet.html +1 -1
- package/dist/docs/types/tools_ai.AIUsage.html +5 -5
- package/dist/docs/types/tools_ai.AIUserMessage.html +2 -2
- package/dist/docs/types/tools_ai.DataContent.html +1 -1
- package/dist/docs/types/tools_ai.ModelPreferences.html +4 -4
- package/dist/docs/types/tools_integrations.ArchiveLinkFilter.html +5 -5
- package/dist/docs/types/tools_integrations.AuthToken.html +4 -4
- package/dist/docs/types/tools_integrations.Authorization.html +4 -4
- package/dist/docs/types/tools_integrations.ComposeConfig.html +4 -4
- package/dist/docs/types/tools_integrations.ContactRoleConfig.html +5 -5
- package/dist/docs/types/tools_integrations.LinkTypeConfig.html +14 -23
- package/dist/docs/types/tools_integrations.SyncContext.html +3 -3
- package/dist/docs/types/tools_plot.SearchOptions.html +5 -6
- package/dist/docs/types/tools_twists.TwistPermissions.html +1 -1
- package/dist/llm-docs/connector.d.ts +1 -1
- package/dist/llm-docs/connector.d.ts.map +1 -1
- package/dist/llm-docs/connector.js +1 -1
- package/dist/llm-docs/connector.js.map +1 -1
- package/dist/llm-docs/plot.d.ts +1 -1
- package/dist/llm-docs/plot.d.ts.map +1 -1
- package/dist/llm-docs/plot.js +1 -1
- package/dist/llm-docs/plot.js.map +1 -1
- package/dist/llm-docs/schedule.d.ts +1 -1
- package/dist/llm-docs/schedule.d.ts.map +1 -1
- package/dist/llm-docs/schedule.js +1 -1
- package/dist/llm-docs/schedule.js.map +1 -1
- package/dist/llm-docs/tools/ai.d.ts +1 -1
- package/dist/llm-docs/tools/ai.d.ts.map +1 -1
- package/dist/llm-docs/tools/ai.js +1 -1
- package/dist/llm-docs/tools/ai.js.map +1 -1
- package/dist/llm-docs/tools/files.d.ts +1 -1
- package/dist/llm-docs/tools/files.d.ts.map +1 -1
- package/dist/llm-docs/tools/files.js +1 -1
- package/dist/llm-docs/tools/files.js.map +1 -1
- package/dist/llm-docs/tools/integrations.d.ts +1 -1
- package/dist/llm-docs/tools/integrations.d.ts.map +1 -1
- package/dist/llm-docs/tools/integrations.js +1 -1
- package/dist/llm-docs/tools/integrations.js.map +1 -1
- package/dist/llm-docs/tools/plot.d.ts +1 -1
- package/dist/llm-docs/tools/plot.d.ts.map +1 -1
- package/dist/llm-docs/tools/plot.js +1 -1
- package/dist/llm-docs/tools/plot.js.map +1 -1
- package/dist/llm-docs/tools/twists.d.ts +1 -1
- package/dist/llm-docs/tools/twists.d.ts.map +1 -1
- package/dist/llm-docs/tools/twists.js +1 -1
- package/dist/llm-docs/tools/twists.js.map +1 -1
- package/dist/llm-docs/twist.d.ts +1 -1
- package/dist/llm-docs/twist.d.ts.map +1 -1
- package/dist/llm-docs/twist.js +1 -1
- package/dist/llm-docs/twist.js.map +1 -1
- package/dist/plot.d.ts +62 -82
- package/dist/plot.d.ts.map +1 -1
- package/dist/plot.js +1 -1
- package/dist/plot.js.map +1 -1
- package/dist/schedule.d.ts +1 -1
- package/dist/tools/ai.d.ts +46 -10
- package/dist/tools/ai.d.ts.map +1 -1
- package/dist/tools/ai.js.map +1 -1
- package/dist/tools/files.d.ts +1 -1
- package/dist/tools/integrations.d.ts +7 -23
- package/dist/tools/integrations.d.ts.map +1 -1
- package/dist/tools/integrations.js.map +1 -1
- package/dist/tools/plot.d.ts +66 -56
- package/dist/tools/plot.d.ts.map +1 -1
- package/dist/tools/plot.js +14 -14
- package/dist/tools/plot.js.map +1 -1
- package/dist/tools/twists.d.ts +2 -2
- package/dist/twist.d.ts +3 -3
- package/dist/twist.js +3 -3
- package/package.json +1 -1
- package/src/connector.ts +19 -4
- package/src/llm-docs/connector.ts +1 -1
- package/src/llm-docs/plot.ts +1 -1
- package/src/llm-docs/schedule.ts +1 -1
- package/src/llm-docs/tools/ai.ts +1 -1
- package/src/llm-docs/tools/files.ts +1 -1
- package/src/llm-docs/tools/integrations.ts +1 -1
- package/src/llm-docs/tools/plot.ts +1 -1
- package/src/llm-docs/tools/twists.ts +1 -1
- package/src/llm-docs/twist.ts +1 -1
- package/src/plot.ts +63 -72
- package/src/schedule.ts +1 -1
- package/src/tools/ai.ts +46 -10
- package/src/tools/files.ts +1 -1
- package/src/tools/integrations.ts +7 -23
- package/src/tools/plot.ts +69 -59
- package/src/tools/twists.ts +2 -2
- package/src/twist.ts +3 -3
- package/dist/docs/types/plot.NewPriority.html +0 -15
- package/dist/docs/types/plot.Priority.html +0 -15
- package/dist/docs/types/plot.PriorityUpdate.html +0 -5
package/dist/tools/plot.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Action, type Thread, type ThreadUpdate, type Actor, type ActorId, ITool, type Link, type LinkUpdate, type NewThread, type NewThreadWithNotes, type NewNote, type
|
|
1
|
+
import { type Action, type Thread, type ThreadUpdate, type Actor, type ActorId, ITool, type Link, type LinkUpdate, type NewThread, type NewThreadWithNotes, type NewNote, type NewFocus, type Note, type NoteUpdate, type PlanOperation, type Focus, type FocusUpdate, Uuid } from "..";
|
|
2
2
|
import { type Schedule, type NewSchedule } from "../schedule";
|
|
3
3
|
import type { Callback } from "./callbacks";
|
|
4
4
|
export declare enum ThreadAccess {
|
|
@@ -15,22 +15,22 @@ export declare enum ThreadAccess {
|
|
|
15
15
|
Create = 1,
|
|
16
16
|
/**
|
|
17
17
|
* List/query all Threads owned by the twist's user.
|
|
18
|
-
* Update any Thread (title, tags, archived, type,
|
|
18
|
+
* Update any Thread (title, tags, archived, type, focus) regardless of creator.
|
|
19
19
|
* Create Notes on any Thread (not just own or mentioned).
|
|
20
20
|
* All Create permissions.
|
|
21
21
|
*/
|
|
22
22
|
Full = 2
|
|
23
23
|
}
|
|
24
|
-
export declare enum
|
|
24
|
+
export declare enum FocusAccess {
|
|
25
25
|
/**
|
|
26
|
-
* Create new
|
|
27
|
-
* Update
|
|
26
|
+
* Create new Focuses for the twist owner.
|
|
27
|
+
* Update Focuses created by the twist.
|
|
28
28
|
*/
|
|
29
29
|
Create = 0,
|
|
30
30
|
/**
|
|
31
|
-
* Read all
|
|
32
|
-
* Create new
|
|
33
|
-
* Update and archive any
|
|
31
|
+
* Read all Focuses owned by the twist's user.
|
|
32
|
+
* Create new Focuses for the twist owner.
|
|
33
|
+
* Update and archive any Focus owned by the twist's user.
|
|
34
34
|
*/
|
|
35
35
|
Full = 1
|
|
36
36
|
}
|
|
@@ -74,7 +74,7 @@ type SearchResultBase = {
|
|
|
74
74
|
id: string;
|
|
75
75
|
title: string | null;
|
|
76
76
|
};
|
|
77
|
-
|
|
77
|
+
focus: {
|
|
78
78
|
id: string;
|
|
79
79
|
title: string | null;
|
|
80
80
|
};
|
|
@@ -103,17 +103,16 @@ export type SearchOptions = {
|
|
|
103
103
|
/** Minimum similarity score 0-1 (default: 0.3) */
|
|
104
104
|
threshold?: number;
|
|
105
105
|
/**
|
|
106
|
-
* Scope search to this
|
|
107
|
-
*
|
|
108
|
-
* priority tree.
|
|
106
|
+
* Scope search to this focus. Must be owned by the twist owner. When
|
|
107
|
+
* omitted, the server scopes the search across all of the owner's focuses.
|
|
109
108
|
*/
|
|
110
|
-
|
|
109
|
+
focusId?: string;
|
|
111
110
|
};
|
|
112
111
|
/**
|
|
113
112
|
* Built-in tool for interacting with the core Plot data layer.
|
|
114
113
|
*
|
|
115
114
|
* The Plot tool provides twists with the ability to create and manage threads,
|
|
116
|
-
*
|
|
115
|
+
* focuses, and contacts within the Plot system. This is the primary interface
|
|
117
116
|
* for twists to persist data and interact with the Plot database.
|
|
118
117
|
*
|
|
119
118
|
* @example
|
|
@@ -174,8 +173,8 @@ export declare abstract class Plot extends ITool {
|
|
|
174
173
|
* }],
|
|
175
174
|
* },
|
|
176
175
|
* link: true,
|
|
177
|
-
*
|
|
178
|
-
* access:
|
|
176
|
+
* focus: {
|
|
177
|
+
* access: FocusAccess.Full
|
|
179
178
|
* },
|
|
180
179
|
* contact: {
|
|
181
180
|
* access: ContactAccess.Read
|
|
@@ -218,14 +217,35 @@ export declare abstract class Plot extends ITool {
|
|
|
218
217
|
* ```
|
|
219
218
|
*/
|
|
220
219
|
intents?: NoteIntentHandler[];
|
|
220
|
+
/**
|
|
221
|
+
* Single conversational handler for mentions.
|
|
222
|
+
*
|
|
223
|
+
* When set, EVERY mention of this twist is routed directly to this
|
|
224
|
+
* handler — intent matching (and the built-in "What can you do?" /
|
|
225
|
+
* "Remove yourself" intents) is skipped. Use this to build a
|
|
226
|
+
* general-purpose conversational assistant that responds to any
|
|
227
|
+
* request, rather than classifying into a fixed set of `intents`.
|
|
228
|
+
*
|
|
229
|
+
* `handler` and `intents` are mutually exclusive; when both are
|
|
230
|
+
* present, `handler` takes precedence and `intents` is ignored.
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```typescript
|
|
234
|
+
* note: {
|
|
235
|
+
* defaultMention: true,
|
|
236
|
+
* handler: this.respond, // (note: Note) => Promise<void>
|
|
237
|
+
* }
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
handler?: (note: Note) => Promise<void>;
|
|
221
241
|
};
|
|
222
242
|
/** Enable link processing from connected source channels. */
|
|
223
243
|
link?: true | {
|
|
224
244
|
/** Access level for links. When omitted with `link: true`, only source channel links are accessible. */
|
|
225
245
|
access?: LinkAccess;
|
|
226
246
|
};
|
|
227
|
-
|
|
228
|
-
access?:
|
|
247
|
+
focus?: {
|
|
248
|
+
access?: FocusAccess;
|
|
229
249
|
};
|
|
230
250
|
contact?: {
|
|
231
251
|
access?: ContactAccess;
|
|
@@ -233,7 +253,7 @@ export declare abstract class Plot extends ITool {
|
|
|
233
253
|
/** Enable semantic search across notes and links owned by the twist's user. */
|
|
234
254
|
search?: true;
|
|
235
255
|
/**
|
|
236
|
-
* When true, admin write operations (on threads/notes/links/
|
|
256
|
+
* When true, admin write operations (on threads/notes/links/focuses not created by this twist)
|
|
237
257
|
* require user approval via plan actions instead of executing immediately.
|
|
238
258
|
* Read operations and operations on the twist's own content still work directly.
|
|
239
259
|
*/
|
|
@@ -275,8 +295,7 @@ export declare abstract class Plot extends ITool {
|
|
|
275
295
|
* For tags, provide a Record<number, boolean> where true adds a tag and false removes it.
|
|
276
296
|
* Tags not included in the update remain unchanged.
|
|
277
297
|
*
|
|
278
|
-
*
|
|
279
|
-
* maintain the correct hierarchical structure.
|
|
298
|
+
* Set `focus` to move the thread to a different focus.
|
|
280
299
|
*
|
|
281
300
|
* Scheduling is handled separately via `createSchedule()` / `updateSchedule()`.
|
|
282
301
|
*
|
|
@@ -435,40 +454,40 @@ export declare abstract class Plot extends ITool {
|
|
|
435
454
|
key: string;
|
|
436
455
|
}): Promise<Note | null>;
|
|
437
456
|
/**
|
|
438
|
-
* Creates a new
|
|
457
|
+
* Creates a new focus in the Plot system.
|
|
439
458
|
*
|
|
440
|
-
*
|
|
441
|
-
* The created
|
|
459
|
+
* Focuses serve as organizational containers for threads and twists.
|
|
460
|
+
* The created focus will be automatically assigned a unique ID.
|
|
442
461
|
*
|
|
443
|
-
* @param
|
|
444
|
-
* @returns Promise resolving to the complete created
|
|
462
|
+
* @param focus - The focus data to create
|
|
463
|
+
* @returns Promise resolving to the complete created focus
|
|
445
464
|
*/
|
|
446
|
-
abstract
|
|
465
|
+
abstract createFocus(focus: NewFocus): Promise<Focus & {
|
|
447
466
|
created: boolean;
|
|
448
467
|
}>;
|
|
449
468
|
/**
|
|
450
|
-
* Retrieves a
|
|
469
|
+
* Retrieves a focus by ID or key.
|
|
451
470
|
*
|
|
452
|
-
* Archived
|
|
471
|
+
* Archived focuses are included in the results.
|
|
453
472
|
*
|
|
454
|
-
* @param
|
|
455
|
-
* @returns Promise resolving to the matching
|
|
473
|
+
* @param focus - Focus lookup by ID or key
|
|
474
|
+
* @returns Promise resolving to the matching focus or null if not found
|
|
456
475
|
*/
|
|
457
|
-
abstract
|
|
476
|
+
abstract getFocus(focus: {
|
|
458
477
|
id: Uuid;
|
|
459
478
|
} | {
|
|
460
479
|
key: string;
|
|
461
|
-
}): Promise<
|
|
480
|
+
}): Promise<Focus | null>;
|
|
462
481
|
/**
|
|
463
|
-
* Updates an existing
|
|
482
|
+
* Updates an existing focus in the Plot system.
|
|
464
483
|
*
|
|
465
|
-
* The
|
|
484
|
+
* The focus is identified by either its ID or key.
|
|
466
485
|
* Only the fields specified in the update will be changed.
|
|
467
486
|
*
|
|
468
|
-
* @param update -
|
|
487
|
+
* @param update - Focus update containing ID/key and fields to change
|
|
469
488
|
* @returns Promise that resolves when the update is complete
|
|
470
489
|
*/
|
|
471
|
-
abstract
|
|
490
|
+
abstract updateFocus(update: FocusUpdate): Promise<void>;
|
|
472
491
|
/**
|
|
473
492
|
* Retrieves actors by their IDs.
|
|
474
493
|
*
|
|
@@ -553,12 +572,10 @@ export declare abstract class Plot extends ITool {
|
|
|
553
572
|
*/
|
|
554
573
|
abstract getThreads(options?: {
|
|
555
574
|
/**
|
|
556
|
-
*
|
|
557
|
-
* When omitted, defaults to the owner's
|
|
575
|
+
* Focus to list threads from. Must be owned by the twist owner.
|
|
576
|
+
* When omitted, defaults to the owner's Inbox.
|
|
558
577
|
*/
|
|
559
|
-
|
|
560
|
-
/** Include threads from descendant priorities. Default: true. */
|
|
561
|
-
includeDescendants?: boolean;
|
|
578
|
+
focusId?: Uuid;
|
|
562
579
|
/** Include archived threads. Default: false. */
|
|
563
580
|
includeArchived?: boolean;
|
|
564
581
|
/** Maximum number of threads to return. Default: 50, max: 200. */
|
|
@@ -567,24 +584,17 @@ export declare abstract class Plot extends ITool {
|
|
|
567
584
|
offset?: number;
|
|
568
585
|
}): Promise<Thread[]>;
|
|
569
586
|
/**
|
|
570
|
-
* Lists
|
|
587
|
+
* Lists focuses owned by the twist's user.
|
|
571
588
|
*
|
|
572
|
-
* Requires `
|
|
589
|
+
* Requires `FocusAccess.Full`.
|
|
573
590
|
*
|
|
574
|
-
* @param options - Query options for filtering
|
|
575
|
-
* @returns Promise resolving to array of
|
|
591
|
+
* @param options - Query options for filtering focuses
|
|
592
|
+
* @returns Promise resolving to array of focuses
|
|
576
593
|
*/
|
|
577
|
-
abstract
|
|
578
|
-
/**
|
|
579
|
-
* Parent priority to list children of. Must be owned by the twist
|
|
580
|
-
* owner. When omitted, defaults to the owner's root priority.
|
|
581
|
-
*/
|
|
582
|
-
parentId?: Uuid;
|
|
583
|
-
/** Include all descendants, not just direct children. Default: false. */
|
|
584
|
-
includeDescendants?: boolean;
|
|
585
|
-
/** Include archived priorities. Default: false. */
|
|
594
|
+
abstract getFocuses(options?: {
|
|
595
|
+
/** Include archived focuses. Default: false. */
|
|
586
596
|
includeArchived?: boolean;
|
|
587
|
-
}): Promise<
|
|
597
|
+
}): Promise<Focus[]>;
|
|
588
598
|
/**
|
|
589
599
|
* Updates a link.
|
|
590
600
|
*
|
package/dist/tools/plot.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plot.d.ts","sourceRoot":"","sources":["../../src/tools/plot.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EACX,KAAK,MAAM,EACX,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,OAAO,EACZ,KAAK,EACL,KAAK,IAAI,EACT,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,kBAAkB,EACvB,KAAK,OAAO,EACZ,KAAK,
|
|
1
|
+
{"version":3,"file":"plot.d.ts","sourceRoot":"","sources":["../../src/tools/plot.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/plot.js
CHANGED
|
@@ -14,26 +14,26 @@ export var ThreadAccess;
|
|
|
14
14
|
ThreadAccess[ThreadAccess["Create"] = 1] = "Create";
|
|
15
15
|
/**
|
|
16
16
|
* List/query all Threads owned by the twist's user.
|
|
17
|
-
* Update any Thread (title, tags, archived, type,
|
|
17
|
+
* Update any Thread (title, tags, archived, type, focus) regardless of creator.
|
|
18
18
|
* Create Notes on any Thread (not just own or mentioned).
|
|
19
19
|
* All Create permissions.
|
|
20
20
|
*/
|
|
21
21
|
ThreadAccess[ThreadAccess["Full"] = 2] = "Full";
|
|
22
22
|
})(ThreadAccess || (ThreadAccess = {}));
|
|
23
|
-
export var
|
|
24
|
-
(function (
|
|
23
|
+
export var FocusAccess;
|
|
24
|
+
(function (FocusAccess) {
|
|
25
25
|
/**
|
|
26
|
-
* Create new
|
|
27
|
-
* Update
|
|
26
|
+
* Create new Focuses for the twist owner.
|
|
27
|
+
* Update Focuses created by the twist.
|
|
28
28
|
*/
|
|
29
|
-
|
|
29
|
+
FocusAccess[FocusAccess["Create"] = 0] = "Create";
|
|
30
30
|
/**
|
|
31
|
-
* Read all
|
|
32
|
-
* Create new
|
|
33
|
-
* Update and archive any
|
|
31
|
+
* Read all Focuses owned by the twist's user.
|
|
32
|
+
* Create new Focuses for the twist owner.
|
|
33
|
+
* Update and archive any Focus owned by the twist's user.
|
|
34
34
|
*/
|
|
35
|
-
|
|
36
|
-
})(
|
|
35
|
+
FocusAccess[FocusAccess["Full"] = 1] = "Full";
|
|
36
|
+
})(FocusAccess || (FocusAccess = {}));
|
|
37
37
|
export var ContactAccess;
|
|
38
38
|
(function (ContactAccess) {
|
|
39
39
|
/** Read existing contact details. Without this, only the ID will be provided. */
|
|
@@ -54,7 +54,7 @@ export const SEARCH_MAX_LIMIT = 30;
|
|
|
54
54
|
* Built-in tool for interacting with the core Plot data layer.
|
|
55
55
|
*
|
|
56
56
|
* The Plot tool provides twists with the ability to create and manage threads,
|
|
57
|
-
*
|
|
57
|
+
* focuses, and contacts within the Plot system. This is the primary interface
|
|
58
58
|
* for twists to persist data and interact with the Plot database.
|
|
59
59
|
*
|
|
60
60
|
* @example
|
|
@@ -115,8 +115,8 @@ export class Plot extends ITool {
|
|
|
115
115
|
* }],
|
|
116
116
|
* },
|
|
117
117
|
* link: true,
|
|
118
|
-
*
|
|
119
|
-
* access:
|
|
118
|
+
* focus: {
|
|
119
|
+
* access: FocusAccess.Full
|
|
120
120
|
* },
|
|
121
121
|
* contact: {
|
|
122
122
|
* access: ContactAccess.Read
|
package/dist/tools/plot.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plot.js","sourceRoot":"","sources":["../../src/tools/plot.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,GAaN,MAAM,IAAI,CAAC;AAOZ,MAAM,CAAN,IAAY,YAmBX;AAnBD,WAAY,YAAY;IACtB;;;OAGG;IACH,qDAAO,CAAA;IACP;;;;OAIG;IACH,mDAAM,CAAA;IACN;;;;;OAKG;IACH,+CAAI,CAAA;AACN,CAAC,EAnBW,YAAY,KAAZ,YAAY,QAmBvB;AAED,MAAM,CAAN,IAAY,
|
|
1
|
+
{"version":3,"file":"plot.js","sourceRoot":"","sources":["../../src/tools/plot.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,GAaN,MAAM,IAAI,CAAC;AAOZ,MAAM,CAAN,IAAY,YAmBX;AAnBD,WAAY,YAAY;IACtB;;;OAGG;IACH,qDAAO,CAAA;IACP;;;;OAIG;IACH,mDAAM,CAAA;IACN;;;;;OAKG;IACH,+CAAI,CAAA;AACN,CAAC,EAnBW,YAAY,KAAZ,YAAY,QAmBvB;AAED,MAAM,CAAN,IAAY,WAYX;AAZD,WAAY,WAAW;IACrB;;;OAGG;IACH,iDAAM,CAAA;IACN;;;;OAIG;IACH,6CAAI,CAAA;AACN,CAAC,EAZW,WAAW,KAAX,WAAW,QAYtB;AAED,MAAM,CAAN,IAAY,aAGX;AAHD,WAAY,aAAa;IACvB,iFAAiF;IACjF,iDAAI,CAAA;AACN,CAAC,EAHW,aAAa,KAAb,aAAa,QAGxB;AAED,MAAM,CAAN,IAAY,UAKX;AALD,WAAY,UAAU;IACpB,0DAA0D;IAC1D,2CAAI,CAAA;IACJ,6FAA6F;IAC7F,2CAAI,CAAA;AACN,CAAC,EALW,UAAU,KAAV,UAAU,QAKrB;AAmDD,gDAAgD;AAChD,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AACvC,+CAA+C;AAC/C,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAcnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,OAAgB,IAAK,SAAQ,KAAK;IACtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2CG;IACH,MAAM,CAAU,OAAO,CA0ErB;CAgZH"}
|
package/dist/tools/twists.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export type Log = {
|
|
|
42
42
|
* },
|
|
43
43
|
* "plot": {
|
|
44
44
|
* "thread:mentioned": ["read", "write", "update"],
|
|
45
|
-
* "
|
|
45
|
+
* "focus": ["read", "write", "update"]
|
|
46
46
|
* }
|
|
47
47
|
* }
|
|
48
48
|
* ```
|
|
@@ -72,7 +72,7 @@ export type TwistPermissions = Record<string, Record<string, string[]>>;
|
|
|
72
72
|
*/
|
|
73
73
|
export declare abstract class Twists extends ITool {
|
|
74
74
|
/**
|
|
75
|
-
* Creates a new twist ID and grants access to people in the current
|
|
75
|
+
* Creates a new twist ID and grants access to people in the current focus.
|
|
76
76
|
*
|
|
77
77
|
* @returns Promise resolving to the generated twist ID
|
|
78
78
|
* @throws When twist creation fails
|
package/dist/twist.d.ts
CHANGED
|
@@ -9,9 +9,9 @@ import type { InferTools, ToolBuilder, ToolShed } from "./utils/types";
|
|
|
9
9
|
* Base class for all twists.
|
|
10
10
|
*
|
|
11
11
|
* A twist is installed at the workspace level and is owned by a single user
|
|
12
|
-
* (see `this.userId`). It has no inherent
|
|
13
|
-
* links it creates are filed against the owner's
|
|
14
|
-
*
|
|
12
|
+
* (see `this.userId`). It has no inherent focus scope: threads, notes, and
|
|
13
|
+
* links it creates are filed against the owner's focuses, with automatic
|
|
14
|
+
* focus matching when no explicit target is provided.
|
|
15
15
|
*
|
|
16
16
|
* Override `build()` to declare tool dependencies and lifecycle methods to
|
|
17
17
|
* handle events.
|
package/dist/twist.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Base class for all twists.
|
|
3
3
|
*
|
|
4
4
|
* A twist is installed at the workspace level and is owned by a single user
|
|
5
|
-
* (see `this.userId`). It has no inherent
|
|
6
|
-
* links it creates are filed against the owner's
|
|
7
|
-
*
|
|
5
|
+
* (see `this.userId`). It has no inherent focus scope: threads, notes, and
|
|
6
|
+
* links it creates are filed against the owner's focuses, with automatic
|
|
7
|
+
* focus matching when no explicit target is provided.
|
|
8
8
|
*
|
|
9
9
|
* Override `build()` to declare tool dependencies and lifecycle methods to
|
|
10
10
|
* handle events.
|
package/package.json
CHANGED
package/src/connector.ts
CHANGED
|
@@ -100,6 +100,20 @@ export type ResolvedRecipient = {
|
|
|
100
100
|
name: string | null;
|
|
101
101
|
/** Platform-specific account identifier pre-resolved at dispatch time (e.g. Slack `U…`, LinkedIn URN, Gmail email address) */
|
|
102
102
|
externalAccountId: string;
|
|
103
|
+
/**
|
|
104
|
+
* The contact's role on the originating thread, resolved from
|
|
105
|
+
* `thread.contact_meta` against the link type's `contactRoles` (e.g.
|
|
106
|
+
* `"to"` / `"cc"` / `"bcc"` for Gmail). `null` when the contact had no
|
|
107
|
+
* explicit role entry — connectors should treat `null` as the link
|
|
108
|
+
* type's default role (the `contactRoles` entry marked `default: true`).
|
|
109
|
+
*
|
|
110
|
+
* Connectors that distinguish roles MUST honor this when addressing the
|
|
111
|
+
* recipient — e.g. Gmail must place `"cc"`/`"bcc"` recipients in the
|
|
112
|
+
* Cc/Bcc headers, never the To header, so BCC recipients are not
|
|
113
|
+
* exposed to the other recipients. Connectors that don't distinguish
|
|
114
|
+
* roles (Slack, Linear) can ignore it.
|
|
115
|
+
*/
|
|
116
|
+
role: string | null;
|
|
103
117
|
};
|
|
104
118
|
|
|
105
119
|
/**
|
|
@@ -138,11 +152,12 @@ export type CreateLinkDraft = {
|
|
|
138
152
|
* `"contacts"` or `"addresses"`.
|
|
139
153
|
*
|
|
140
154
|
* Only populated for those link types; otherwise undefined. Each entry contains the Plot
|
|
141
|
-
* contact UUID
|
|
155
|
+
* contact UUID, the platform-specific account ID
|
|
142
156
|
* (`externalAccountId`) the connector should use to address the
|
|
143
|
-
* recipient without performing its own lookup
|
|
144
|
-
*
|
|
145
|
-
* `
|
|
157
|
+
* recipient without performing its own lookup, and the contact's
|
|
158
|
+
* `role` on the thread (e.g. `"to"` / `"cc"` / `"bcc"`) resolved from
|
|
159
|
+
* `thread.contact_meta`. For `"addresses"` link types, contacts without
|
|
160
|
+
* a connection-scoped row fall back to `contact.email`.
|
|
146
161
|
*/
|
|
147
162
|
recipients?: ResolvedRecipient[];
|
|
148
163
|
/**
|
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
* Generated from: prebuild.ts
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
export default "import { type Actor, type ActorId, type Contact, type Link, type NewLinkWithNotes, type Note, type Thread, type Uuid } from \"./plot\";\nimport type { ScheduleContactStatus } from \"./schedule\";\nimport {\n type AuthProvider,\n type AuthToken,\n type Authorization,\n type Channel,\n type LinkTypeConfig,\n type SyncContext,\n} from \"./tools/integrations\";\nimport { Twist } from \"./twist\";\n\n/**\n * Declares how a connector's platform handles emoji reactions.\n *\n * Drives Plot UI behavior (e.g. the picker filters the available\n * reactions on notes whose primary connector declares `fixed`) and\n * outbound dispatch (Plot won't try to push an emoji the platform\n * can't accept).\n *\n * Variants:\n * - `open-unicode`: Platform accepts any Unicode emoji. `customEmoji`\n * indicates whether the platform additionally supports workspace\n * custom emoji (Slack, Google Chat).\n * - `unicode-subset`: Platform accepts Unicode but only a finite set.\n * `subset` lists the allowed emoji (omit for \"currently full Unicode\n * per docs, future-proofed for shrinkage\").\n * - `fixed`: Platform only accepts a fixed set (e.g. LinkedIn\n * Messaging's 7-reaction set). `allowed` lists every supported emoji.\n */\nexport type ReactionCapabilities =\n | { mode: \"open-unicode\"; customEmoji?: \"workspace\" | \"none\" }\n | { mode: \"unicode-subset\"; subset?: readonly string[] }\n | { mode: \"fixed\"; allowed: readonly string[] };\n\n/**\n * Result returned from {@link Connector.onNoteCreated} and\n * {@link Connector.onNoteUpdated} to report what the external system now\n * has stored for the note.\n *\n * The runtime hashes `externalContent` and stores it as the note's sync\n * baseline. On the next sync-in, if the incoming content hashes to the\n * same value, the runtime knows the external side hasn't changed and\n * preserves Plot's (possibly formatted) content. When the external side\n * is edited, the hash diverges and the runtime overwrites Plot's content\n * with the new external version.\n *\n * Omitting `externalContent` skips baseline tracking — the next sync-in\n * will overwrite Plot's content (previous behavior). Always provide it\n * when the write-back's return value reflects what the external system\n * actually stored (often lossy plain-text), so the round-trip does not\n * clobber the original Plot markdown.\n *\n * The hash covers only the content string — the runtime intentionally\n * does not include a content-type in the hash, so write-back and sync-in\n * do not have to agree on a content-type label for the same underlying\n * bytes. Return exactly the string your connector's sync-in path will\n * emit as `NewNote.content` for this note on the next re-ingest.\n *\n * For back-compat, `onNoteCreated` may also return a plain string, which\n * is treated as `{ key }` with no baseline.\n */\nexport type NoteWriteBackResult = {\n /**\n * External system identifier assigned to this note. Set as the note's\n * `key` for future upsert matching. Required when the runtime does not\n * already know the key (i.e., from `onNoteCreated`); ignored from\n * `onNoteUpdated` when the key was already established on create.\n */\n key?: string;\n /**\n * The content string as the external system now stores it, post-write.\n * For systems whose write-back returns a representation of what was\n * actually stored (e.g. Google Drive comment `content` after a create),\n * pass that verbatim. For systems that only accept plain text, this\n * will often be a lossy plain-text version of the Plot markdown — that\n * is exactly the point: storing the lossy form as baseline lets the\n * next sync-in recognize it and skip overwriting the richer Plot\n * version.\n *\n * Must exactly match the string your connector's sync-in path emits as\n * `NewNote.content` for this note on re-ingest.\n */\n externalContent?: string;\n};\n\n/**\n * A Plot contact pre-resolved to its platform account ID, ready for use\n * in a messaging dispatch.\n *\n * Populated by the runtime for link types with `compose.targets: \"contacts\"` before\n * `onCreateLink` is called. The connector should use `externalAccountId`\n * directly to address the recipient on the platform (e.g. Slack user ID,\n * LinkedIn URN, Gmail address) without performing its own contact lookup.\n */\nexport type ResolvedRecipient = {\n /** Plot contact UUID */\n id: Uuid;\n /** Display name, or null if not set */\n name: string | null;\n /** Platform-specific account identifier pre-resolved at dispatch time (e.g. Slack `U…`, LinkedIn URN, Gmail email address) */\n externalAccountId: string;\n};\n\n/**\n * Fields captured in Plot when a user initiates creation of a new external\n * item via a connector's `onCreateLink` hook.\n *\n * Thread-agnostic on purpose — connectors do not receive the Plot thread.\n * The platform attaches the returned `NewLinkWithNotes` to the originating\n * thread once `onCreateLink` resolves.\n */\nexport type CreateLinkDraft = {\n /** The channel (account + resource) the new item belongs to. */\n channelId: string;\n /** Link type identifier, matches a `LinkTypeConfig.type`. */\n type: string;\n /** Status the user selected. Matches a `statuses[].status` for `type`. */\n status: string;\n /** Title of the originating Plot thread (post AI title generation). */\n title: string;\n /** Markdown content of the thread's first note, or null if none. */\n noteContent: string | null;\n /**\n * Contacts attached to the originating Plot thread, excluding the\n * creating user. Use these as recipients (email, chat DM members, etc.)\n * when the external item is a message or invite. An empty list means\n * the user did not add anyone to the thread.\n *\n * For link types with `compose.targets: \"contacts\"`, prefer `recipients` over\n * re-resolving contacts yourself: the runtime pre-resolves each contact\n * to its platform account ID (`externalAccountId`) and populates\n * `recipients` before `onCreateLink` is called.\n */\n contacts: Actor[];\n /**\n * Pre-resolved recipients for link types whose `compose.targets` is\n * `\"contacts\"` or `\"addresses\"`.\n *\n * Only populated for those link types; otherwise undefined. Each entry contains the Plot\n * contact UUID and the platform-specific account ID\n * (`externalAccountId`) the connector should use to address the\n * recipient without performing its own lookup. For `\"addresses\"` link\n * types, contacts without a connection-scoped row fall back to\n * `contact.email`.\n */\n recipients?: ResolvedRecipient[];\n /**\n * Free-form addresses the user typed into the picker (no Plot contact\n * row). Only populated for link types with `compose.targets: \"addresses\"`; otherwise\n * undefined. Connectors should append these alongside `recipients`\n * when constructing the recipient list (e.g. `To:` header for Gmail).\n */\n inviteEmails?: string[];\n};\n\n/**\n * Base class for connectors — twists that sync data from external services.\n *\n * Connectors declare a single OAuth provider and scopes, and implement channel\n * lifecycle methods for discovering and syncing external resources. They save\n * data directly via `integrations.saveLink()` instead of using the Plot tool.\n *\n * @example\n * ```typescript\n * class LinearConnector extends Connector<LinearConnector> {\n * readonly provider = AuthProvider.Linear;\n * readonly scopes = [\"read\", \"write\"];\n * readonly linkTypes = [{\n * type: \"issue\",\n * label: \"Issue\",\n * statuses: [\n * { status: \"open\", label: \"Open\" },\n * { status: \"done\", label: \"Done\" },\n * ],\n * }];\n *\n * build(build: ToolBuilder) {\n * return {\n * integrations: build(Integrations),\n * };\n * }\n *\n * async getChannels(auth: Authorization, token: AuthToken): Promise<Channel[]> {\n * const teams = await this.listTeams(token);\n * return teams.map(t => ({ id: t.id, title: t.name }));\n * }\n *\n * async onChannelEnabled(channel: Channel) {\n * const issues = await this.fetchIssues(channel.id);\n * for (const issue of issues) {\n * await this.tools.integrations.saveLink(issue);\n * }\n * }\n *\n * async onChannelDisabled(channel: Channel) {\n * // Clean up webhooks, sync state, etc.\n * }\n * }\n * ```\n */\nexport abstract class Connector<TSelf> extends Twist<TSelf> {\n /**\n * Static marker to identify Connector subclasses without instanceof checks\n * across worker boundaries.\n */\n static readonly isConnector = true;\n\n // ---- Identity (abstract — every connector must declare) ----\n\n /** The OAuth provider this connector authenticates with. */\n readonly provider?: AuthProvider;\n\n /** OAuth scopes to request for this connector. */\n readonly scopes?: string[];\n\n // ---- Auth model ----\n\n /**\n * When true, one credential is shared across all users in the workspace,\n * entered once by the installer. When false (default), each user provides\n * their own credential.\n *\n * Applies to both OAuth and key-based connectors:\n * - Shared OAuth: e.g. Slack bot token (workspace-level)\n * - Shared key: e.g. Attio workspace API key\n * - Individual OAuth: e.g. Google Calendar (per-user)\n * - Individual key: e.g. Fellow (per-user API key)\n */\n readonly shared?: boolean;\n\n /**\n * The Options field name that contains the authentication key (e.g. \"apiKey\").\n * Must reference a `secure: true` field in the Options schema.\n *\n * When set, this connector uses key-based auth instead of OAuth.\n * For individual connectors (`shared` is false), this field is stored\n * per-user rather than in shared config.\n */\n readonly keyOption?: string;\n\n // ---- Optional metadata ----\n\n /**\n * When true, this connector has a single implicit channel.\n * `getChannels()` must return exactly one Channel.\n * The UI will show channel config inline instead of a channel list.\n */\n readonly singleChannel?: boolean;\n\n /**\n * Registry of link types this connector creates (e.g., issue, event, message).\n * Used for display in the UI (icons, labels, statuses).\n */\n readonly linkTypes?: LinkTypeConfig[];\n\n /**\n * Declares how this connector's platform handles emoji reactions.\n * Used to filter the reaction picker for notes whose primary connector\n * is this one, and to guard outbound dispatch from sending emoji the\n * platform can't accept.\n *\n * Leave undefined for connectors whose platform has no concept of\n * reactions (calendar, file storage, issue trackers without reactions).\n */\n readonly reactionCapabilities?: ReactionCapabilities;\n\n /**\n * When true, this connector is mentioned by default on replies to threads it created.\n * When false (default), this connector cannot be mentioned at all.\n *\n * Set this to true for connectors with bidirectional sync (e.g., issue trackers,\n * messaging) where user replies should be written back to the external service.\n */\n static readonly handleReplies?: boolean;\n\n // ---- Account identity (abstract — every connector must implement) ----\n\n /**\n * Returns a human-readable name for the connected account.\n * Shown in the connections list and edit modal to identify this connection.\n *\n * For OAuth connectors, this is typically the workspace or organization name\n * (e.g., \"Acme Corp\" for a Linear workspace). For API key connectors, this\n * could be the workspace name from the external service.\n *\n * Override this in your connector to return a meaningful account name.\n *\n * @param auth - The authorization (null for no-provider connectors)\n * @param token - The access token (null for no-provider connectors)\n * @returns Promise resolving to the account display name\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n getAccountName(\n auth: Authorization | null,\n token: AuthToken | null\n ): Promise<string | null> {\n return Promise.resolve(null);\n }\n\n // ---- Channel lifecycle (abstract — every connector must implement) ----\n\n /**\n * Returns available channels for the authorized actor.\n * Called after OAuth is complete, during the setup/edit modal.\n *\n * @param auth - The completed authorization with provider and actor info\n * @param token - The access token for making API calls\n * @returns Promise resolving to available channels for the user to select\n */\n abstract getChannels(\n auth: Authorization | null,\n token: AuthToken | null\n ): Promise<Channel[]>;\n\n /**\n * Called when a channel resource is enabled for syncing.\n *\n * The framework dispatches this in three cases:\n * 1. **Initial enable** — user toggled the channel on for the first time.\n * 2. **Auto-enable** — `setChannels` discovered a new channel on a\n * connection with `auto_enable_new_channels` set.\n * 3. **Recovery after re-auth** — the user re-authorized a previously-\n * broken connection. The framework calls `onChannelEnabled` for every\n * channel that was already enabled at the time of re-auth, with\n * `context.recovering = true`. See {@link SyncContext.recovering}.\n *\n * Implementations should be **idempotent and overwrite stored state**:\n * the same channel may receive multiple `onChannelEnabled` calls across\n * its lifetime. Use unconditional `this.set()` writes rather than\n * coalesce/skip-if-present logic so a recovery dispatch wipes stale\n * cursors and state from the prior session.\n *\n * **Sync state tracking is automatic.** The framework stamps the\n * connection as \"syncing\" when it dispatches this method and clears\n * that state when:\n * - the connector calls `tools.integrations.channelSyncCompleted(id)`\n * once the initial backfill is done, OR\n * - this method throws an unhandled exception (auto-cleared so the UI\n * doesn't get stuck in \"syncing\" forever).\n *\n * **IMPORTANT: This method runs inline in the HTTP request handler.**\n * Any long-running work (webhook setup, API calls, sync) MUST be queued\n * as a separate task via `this.runTask()`, not executed inline. Blocking\n * here causes the client to spin waiting for the response.\n *\n * Only lightweight operations should appear directly in this method:\n * `this.set()`, `this.get()`, `this.callback()`, and `this.runTask()`.\n *\n * @example\n * ```typescript\n * async onChannelEnabled(channel: Channel, context?: SyncContext): Promise<void> {\n * // Recovery: drop stale cursors so the next sync re-walks history.\n * if (context?.recovering) {\n * await this.clear(`last_sync_token_${channel.id}`);\n * }\n *\n * await this.set(`sync_state_${channel.id}`, { channelId: channel.id });\n *\n * // Queue sync as a task — do NOT use this.run() or call sync methods inline\n * const syncCallback = await this.callback(this.syncBatch, 1, \"full\", channel.id, true);\n * await this.runTask(syncCallback);\n *\n * // Queue webhook setup as a task — do NOT call setupWebhook() inline\n * const webhookCallback = await this.callback(this.setupWebhook, channel.id);\n * await this.runTask(webhookCallback);\n * }\n * ```\n *\n * @param channel - The channel that was enabled\n * @param context - Optional sync context (plan-based hints, recovery flag)\n */\n abstract onChannelEnabled(channel: Channel, context?: SyncContext): Promise<void>;\n\n /**\n * Called when a channel resource is disabled.\n * Should stop sync, clean up webhooks, and remove state.\n *\n * @param channel - The channel that was disabled\n */\n abstract onChannelDisabled(channel: Channel): Promise<void>;\n\n // ---- Write-back hooks (optional, default no-ops) ----\n\n /**\n * Called when a link created by this connector is updated by the user.\n * Override to write back changes to the external service\n * (e.g., changing issue status in Linear when marked done in Plot).\n *\n * @param link - The updated link\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onLinkUpdated(link: Link): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a user creates a thread in Plot that should create a new\n * item in this connector's external system.\n *\n * A connector opts in to Plot-initiated creation by declaring a\n * `compose` block on the relevant `LinkTypeConfig` (see\n * {@link ComposeConfig}). When a user picks \"Create new <type>\" from the\n * Add link modal and the thread is synced, the runtime calls this method\n * with the draft fields.\n *\n * Implementations should create the item in the external service and\n * return a `NewLinkWithNotes` describing the created item. The platform\n * attaches the returned link to the originating thread — do not call\n * `integrations.saveLink` yourself.\n *\n * Returning `null` aborts creation silently (the thread is still saved\n * without a link).\n *\n * @param draft - The fields captured in Plot for the new item.\n * @returns The link to attach, or null to abort creation.\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onCreateLink(draft: CreateLinkDraft): Promise<NewLinkWithNotes | null> {\n return Promise.resolve(null);\n }\n\n /**\n * Called when a note is created on a thread owned by this connector.\n * Override to write back comments to the external service\n * (e.g., adding a comment to a Linear issue).\n *\n * Returning a string or {@link NoteWriteBackResult} links the Plot note\n * to its external counterpart. A plain string sets the note's `key`.\n * A `NoteWriteBackResult` additionally sets a sync baseline (via\n * `externalContent`) so the next sync-in can recognize the round-tripped\n * content and preserve Plot's formatted version. See\n * {@link NoteWriteBackResult} for details.\n *\n * @param note - The created note\n * @param thread - The thread the note belongs to (includes thread.meta with connector-specific data)\n * @returns Optional note key or NoteWriteBackResult for external dedup + baseline tracking\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onNoteCreated(note: Note, thread: Thread): Promise<string | NoteWriteBackResult | void> {\n return Promise.resolve();\n }\n\n /**\n * Resolve a `fileRef` action's bytes for download. Called when a user opens\n * an attachment in Plot. Return either a redirect URL (preferred for sources\n * that issue signed URLs, like Linear S3 or Slack permalink_public) or a\n * streamed body (required when bytes are only reachable through an\n * authenticated API call, like Gmail attachments.get).\n *\n * @param ref Opaque value the connector previously emitted on a fileRef action.\n * @returns Either `{ redirectUrl }` or `{ body, mimeType, fileName? }`.\n * @throws If the source is unavailable, the connection is broken, or `ref` is invalid.\n *\n * If not overridden, fileRef actions on this connector's notes will return 410 Gone.\n */\n async downloadAttachment(\n ref: string,\n ): Promise<\n | { redirectUrl: string }\n | { body: ReadableStream | Uint8Array; mimeType: string; fileName?: string }\n > {\n throw new Error(\n `downloadAttachment not implemented for ${this.constructor.name} (ref=${ref})`,\n );\n }\n\n /**\n * Called when a note on a thread owned by this connector is updated.\n * Override to write back changes to the external service\n * (e.g., syncing reaction tags as emoji reactions, or editing a comment\n * whose content changed in Plot).\n *\n * Return a {@link NoteWriteBackResult} with `externalContent` to update\n * the sync baseline after a successful write-back, so the next sync-in\n * recognizes the external version as already-seen and preserves Plot's\n * content.\n *\n * @param note - The updated note (includes current tags)\n * @param thread - The thread the note belongs to (includes thread.meta with connector-specific data)\n * @returns Optional NoteWriteBackResult for baseline tracking\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onNoteUpdated(note: Note, thread: Thread): Promise<NoteWriteBackResult | void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a user reads or unreads a thread owned by this connector.\n * Override to write back read status to the external service\n * (e.g., marking an email as read in Gmail).\n *\n * @param thread - The thread that was read/unread (includes thread.meta with connector-specific data)\n * @param actor - The user who performed the action\n * @param unread - false when marked as read, true when marked as unread\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onThreadRead(thread: Thread, actor: Actor, unread: boolean): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a user adds, removes, or changes the role of contacts on a\n * thread owned by this connector. Override on connectors whose source\n * supports mid-thread recipient changes (Gmail, IMAP, etc.). Connectors\n * that can't change recipients per-message (Slack, Linear) leave this as\n * the default no-op and should also declare\n * `LinkTypeConfig.supportsContactChanges: false`.\n *\n * The dispatch fires after Plot has persisted the change. Connectors are\n * expected to reflect it on the next outbound note (e.g. building To/Cc/Bcc\n * headers from the current `thread.contacts` × `thread.contactMeta`) — this\n * callback is not the right place to send a standalone notification.\n *\n * @param thread - The thread whose contacts changed\n * @param changes - The added/removed contacts and any role transitions on existing contacts\n */\n /* eslint-disable @typescript-eslint/no-unused-vars */\n onContactsChanged(\n thread: Thread,\n changes: {\n added: Array<{ contact: Contact; role: string }>;\n removed: Array<{ contact: Contact; role: string }>;\n changed: Array<{ contact: Contact; from: string; to: string }>;\n },\n ): Promise<void> {\n return Promise.resolve();\n }\n /* eslint-enable @typescript-eslint/no-unused-vars */\n\n /**\n * Called when a user marks or unmarks a thread as todo.\n * Override to sync todo status to the external service\n * (e.g., starring an email in Gmail when marked as todo).\n *\n * @param thread - The thread (includes thread.meta with connector-specific data)\n * @param actor - The user who changed the todo status\n * @param todo - true when marked as todo, false when done or removed\n * @param options - Additional context\n * @param options.date - The todo date (when todo=true)\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onThreadToDo(thread: Thread, actor: Actor, todo: boolean, options: { date?: Date }): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a schedule contact's RSVP status changes on a thread owned by this connector.\n * Override to sync RSVP changes back to the external calendar.\n *\n * @param thread - The thread (includes thread.meta with connector-specific data)\n * @param scheduleId - The schedule ID\n * @param contactId - The contact whose status changed\n * @param status - The new RSVP status ('attend', 'skip', or null)\n * @param actor - The user who changed the status\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onScheduleContactUpdated(thread: Thread, scheduleId: string, contactId: ActorId, status: ScheduleContactStatus | null, actor: Actor): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a user adds or removes a single emoji reaction on a note\n * (one event per `(note, actor, emoji)` state transition).\n *\n * Dispatch is routed to the reacting user's own connector instance via\n * `twist_instance_for_actor` on `note_reaction.actor_id`, so this method\n * already runs under the reactor's auth. Fetch the API client with the\n * connector's normal token-fetch path (`this.tools.integrations.get(...)`)\n * and the external write — e.g. Slack `reactions.add` — will be attributed\n * to the correct user. No `actAs` step required.\n *\n * If the reacting user has no connection of this type, no dispatch fires\n * for that reaction (it stays in Plot only).\n *\n * Override to sync per-actor reactions back to the external system.\n *\n * @param note - The note that was reacted on (partial; `id`, `key`, `content` populated)\n * @param thread - The thread the note belongs to (partial; `id`, `title`, `archived`, `meta` populated)\n * @param actor - The contact who added/removed the reaction\n * @param emoji - The emoji (Unicode grapheme or `provider:workspace/name` custom-emoji ref)\n * @param added - `true` if the reaction is now present, `false` if it was removed\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onNoteReactionChanged(note: Note, thread: Thread, actor: Actor, emoji: string, added: boolean): Promise<void> {\n return Promise.resolve();\n }\n\n // ---- Activation ----\n\n /**\n * Called when the connector is activated after OAuth is complete.\n *\n * Connectors receive the authorization in addition to the activating actor.\n * When this runs, `this.userId` is already populated with the installing\n * user's ID.\n *\n * Default implementation does nothing. Override for custom setup.\n *\n * @param context - The activation context\n * @param context.auth - The completed OAuth authorization\n * @param context.actor - The actor who activated the connector\n */\n // @ts-ignore - Connector.activate() has a Connector-specific context type.\n activate(context: { auth?: Authorization; actor?: Actor }): Promise<void> {\n return Promise.resolve();\n }\n}\n\n/** @deprecated Use `Connector` instead. */\nexport { Connector as Source };\n";
|
|
8
|
+
export default "import { type Actor, type ActorId, type Contact, type Link, type NewLinkWithNotes, type Note, type Thread, type Uuid } from \"./plot\";\nimport type { ScheduleContactStatus } from \"./schedule\";\nimport {\n type AuthProvider,\n type AuthToken,\n type Authorization,\n type Channel,\n type LinkTypeConfig,\n type SyncContext,\n} from \"./tools/integrations\";\nimport { Twist } from \"./twist\";\n\n/**\n * Declares how a connector's platform handles emoji reactions.\n *\n * Drives Plot UI behavior (e.g. the picker filters the available\n * reactions on notes whose primary connector declares `fixed`) and\n * outbound dispatch (Plot won't try to push an emoji the platform\n * can't accept).\n *\n * Variants:\n * - `open-unicode`: Platform accepts any Unicode emoji. `customEmoji`\n * indicates whether the platform additionally supports workspace\n * custom emoji (Slack, Google Chat).\n * - `unicode-subset`: Platform accepts Unicode but only a finite set.\n * `subset` lists the allowed emoji (omit for \"currently full Unicode\n * per docs, future-proofed for shrinkage\").\n * - `fixed`: Platform only accepts a fixed set (e.g. LinkedIn\n * Messaging's 7-reaction set). `allowed` lists every supported emoji.\n */\nexport type ReactionCapabilities =\n | { mode: \"open-unicode\"; customEmoji?: \"workspace\" | \"none\" }\n | { mode: \"unicode-subset\"; subset?: readonly string[] }\n | { mode: \"fixed\"; allowed: readonly string[] };\n\n/**\n * Result returned from {@link Connector.onNoteCreated} and\n * {@link Connector.onNoteUpdated} to report what the external system now\n * has stored for the note.\n *\n * The runtime hashes `externalContent` and stores it as the note's sync\n * baseline. On the next sync-in, if the incoming content hashes to the\n * same value, the runtime knows the external side hasn't changed and\n * preserves Plot's (possibly formatted) content. When the external side\n * is edited, the hash diverges and the runtime overwrites Plot's content\n * with the new external version.\n *\n * Omitting `externalContent` skips baseline tracking — the next sync-in\n * will overwrite Plot's content (previous behavior). Always provide it\n * when the write-back's return value reflects what the external system\n * actually stored (often lossy plain-text), so the round-trip does not\n * clobber the original Plot markdown.\n *\n * The hash covers only the content string — the runtime intentionally\n * does not include a content-type in the hash, so write-back and sync-in\n * do not have to agree on a content-type label for the same underlying\n * bytes. Return exactly the string your connector's sync-in path will\n * emit as `NewNote.content` for this note on the next re-ingest.\n *\n * For back-compat, `onNoteCreated` may also return a plain string, which\n * is treated as `{ key }` with no baseline.\n */\nexport type NoteWriteBackResult = {\n /**\n * External system identifier assigned to this note. Set as the note's\n * `key` for future upsert matching. Required when the runtime does not\n * already know the key (i.e., from `onNoteCreated`); ignored from\n * `onNoteUpdated` when the key was already established on create.\n */\n key?: string;\n /**\n * The content string as the external system now stores it, post-write.\n * For systems whose write-back returns a representation of what was\n * actually stored (e.g. Google Drive comment `content` after a create),\n * pass that verbatim. For systems that only accept plain text, this\n * will often be a lossy plain-text version of the Plot markdown — that\n * is exactly the point: storing the lossy form as baseline lets the\n * next sync-in recognize it and skip overwriting the richer Plot\n * version.\n *\n * Must exactly match the string your connector's sync-in path emits as\n * `NewNote.content` for this note on re-ingest.\n */\n externalContent?: string;\n};\n\n/**\n * A Plot contact pre-resolved to its platform account ID, ready for use\n * in a messaging dispatch.\n *\n * Populated by the runtime for link types with `compose.targets: \"contacts\"` before\n * `onCreateLink` is called. The connector should use `externalAccountId`\n * directly to address the recipient on the platform (e.g. Slack user ID,\n * LinkedIn URN, Gmail address) without performing its own contact lookup.\n */\nexport type ResolvedRecipient = {\n /** Plot contact UUID */\n id: Uuid;\n /** Display name, or null if not set */\n name: string | null;\n /** Platform-specific account identifier pre-resolved at dispatch time (e.g. Slack `U…`, LinkedIn URN, Gmail email address) */\n externalAccountId: string;\n /**\n * The contact's role on the originating thread, resolved from\n * `thread.contact_meta` against the link type's `contactRoles` (e.g.\n * `\"to\"` / `\"cc\"` / `\"bcc\"` for Gmail). `null` when the contact had no\n * explicit role entry — connectors should treat `null` as the link\n * type's default role (the `contactRoles` entry marked `default: true`).\n *\n * Connectors that distinguish roles MUST honor this when addressing the\n * recipient — e.g. Gmail must place `\"cc\"`/`\"bcc\"` recipients in the\n * Cc/Bcc headers, never the To header, so BCC recipients are not\n * exposed to the other recipients. Connectors that don't distinguish\n * roles (Slack, Linear) can ignore it.\n */\n role: string | null;\n};\n\n/**\n * Fields captured in Plot when a user initiates creation of a new external\n * item via a connector's `onCreateLink` hook.\n *\n * Thread-agnostic on purpose — connectors do not receive the Plot thread.\n * The platform attaches the returned `NewLinkWithNotes` to the originating\n * thread once `onCreateLink` resolves.\n */\nexport type CreateLinkDraft = {\n /** The channel (account + resource) the new item belongs to. */\n channelId: string;\n /** Link type identifier, matches a `LinkTypeConfig.type`. */\n type: string;\n /** Status the user selected. Matches a `statuses[].status` for `type`. */\n status: string;\n /** Title of the originating Plot thread (post AI title generation). */\n title: string;\n /** Markdown content of the thread's first note, or null if none. */\n noteContent: string | null;\n /**\n * Contacts attached to the originating Plot thread, excluding the\n * creating user. Use these as recipients (email, chat DM members, etc.)\n * when the external item is a message or invite. An empty list means\n * the user did not add anyone to the thread.\n *\n * For link types with `compose.targets: \"contacts\"`, prefer `recipients` over\n * re-resolving contacts yourself: the runtime pre-resolves each contact\n * to its platform account ID (`externalAccountId`) and populates\n * `recipients` before `onCreateLink` is called.\n */\n contacts: Actor[];\n /**\n * Pre-resolved recipients for link types whose `compose.targets` is\n * `\"contacts\"` or `\"addresses\"`.\n *\n * Only populated for those link types; otherwise undefined. Each entry contains the Plot\n * contact UUID, the platform-specific account ID\n * (`externalAccountId`) the connector should use to address the\n * recipient without performing its own lookup, and the contact's\n * `role` on the thread (e.g. `\"to\"` / `\"cc\"` / `\"bcc\"`) resolved from\n * `thread.contact_meta`. For `\"addresses\"` link types, contacts without\n * a connection-scoped row fall back to `contact.email`.\n */\n recipients?: ResolvedRecipient[];\n /**\n * Free-form addresses the user typed into the picker (no Plot contact\n * row). Only populated for link types with `compose.targets: \"addresses\"`; otherwise\n * undefined. Connectors should append these alongside `recipients`\n * when constructing the recipient list (e.g. `To:` header for Gmail).\n */\n inviteEmails?: string[];\n};\n\n/**\n * Base class for connectors — twists that sync data from external services.\n *\n * Connectors declare a single OAuth provider and scopes, and implement channel\n * lifecycle methods for discovering and syncing external resources. They save\n * data directly via `integrations.saveLink()` instead of using the Plot tool.\n *\n * @example\n * ```typescript\n * class LinearConnector extends Connector<LinearConnector> {\n * readonly provider = AuthProvider.Linear;\n * readonly scopes = [\"read\", \"write\"];\n * readonly linkTypes = [{\n * type: \"issue\",\n * label: \"Issue\",\n * statuses: [\n * { status: \"open\", label: \"Open\" },\n * { status: \"done\", label: \"Done\" },\n * ],\n * }];\n *\n * build(build: ToolBuilder) {\n * return {\n * integrations: build(Integrations),\n * };\n * }\n *\n * async getChannels(auth: Authorization, token: AuthToken): Promise<Channel[]> {\n * const teams = await this.listTeams(token);\n * return teams.map(t => ({ id: t.id, title: t.name }));\n * }\n *\n * async onChannelEnabled(channel: Channel) {\n * const issues = await this.fetchIssues(channel.id);\n * for (const issue of issues) {\n * await this.tools.integrations.saveLink(issue);\n * }\n * }\n *\n * async onChannelDisabled(channel: Channel) {\n * // Clean up webhooks, sync state, etc.\n * }\n * }\n * ```\n */\nexport abstract class Connector<TSelf> extends Twist<TSelf> {\n /**\n * Static marker to identify Connector subclasses without instanceof checks\n * across worker boundaries.\n */\n static readonly isConnector = true;\n\n // ---- Identity (abstract — every connector must declare) ----\n\n /** The OAuth provider this connector authenticates with. */\n readonly provider?: AuthProvider;\n\n /** OAuth scopes to request for this connector. */\n readonly scopes?: string[];\n\n // ---- Auth model ----\n\n /**\n * When true, one credential is shared across all users in the workspace,\n * entered once by the installer. When false (default), each user provides\n * their own credential.\n *\n * Applies to both OAuth and key-based connectors:\n * - Shared OAuth: e.g. Slack bot token (workspace-level)\n * - Shared key: e.g. Attio workspace API key\n * - Individual OAuth: e.g. Google Calendar (per-user)\n * - Individual key: e.g. Fellow (per-user API key)\n */\n readonly shared?: boolean;\n\n /**\n * The Options field name that contains the authentication key (e.g. \"apiKey\").\n * Must reference a `secure: true` field in the Options schema.\n *\n * When set, this connector uses key-based auth instead of OAuth.\n * For individual connectors (`shared` is false), this field is stored\n * per-user rather than in shared config.\n */\n readonly keyOption?: string;\n\n // ---- Optional metadata ----\n\n /**\n * When true, this connector has a single implicit channel.\n * `getChannels()` must return exactly one Channel.\n * The UI will show channel config inline instead of a channel list.\n */\n readonly singleChannel?: boolean;\n\n /**\n * Registry of link types this connector creates (e.g., issue, event, message).\n * Used for display in the UI (icons, labels, statuses).\n */\n readonly linkTypes?: LinkTypeConfig[];\n\n /**\n * Declares how this connector's platform handles emoji reactions.\n * Used to filter the reaction picker for notes whose primary connector\n * is this one, and to guard outbound dispatch from sending emoji the\n * platform can't accept.\n *\n * Leave undefined for connectors whose platform has no concept of\n * reactions (calendar, file storage, issue trackers without reactions).\n */\n readonly reactionCapabilities?: ReactionCapabilities;\n\n /**\n * When true, this connector is mentioned by default on replies to threads it created.\n * When false (default), this connector cannot be mentioned at all.\n *\n * Set this to true for connectors with bidirectional sync (e.g., issue trackers,\n * messaging) where user replies should be written back to the external service.\n */\n static readonly handleReplies?: boolean;\n\n // ---- Account identity (abstract — every connector must implement) ----\n\n /**\n * Returns a human-readable name for the connected account.\n * Shown in the connections list and edit modal to identify this connection.\n *\n * For OAuth connectors, this is typically the workspace or organization name\n * (e.g., \"Acme Corp\" for a Linear workspace). For API key connectors, this\n * could be the workspace name from the external service.\n *\n * Override this in your connector to return a meaningful account name.\n *\n * @param auth - The authorization (null for no-provider connectors)\n * @param token - The access token (null for no-provider connectors)\n * @returns Promise resolving to the account display name\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n getAccountName(\n auth: Authorization | null,\n token: AuthToken | null\n ): Promise<string | null> {\n return Promise.resolve(null);\n }\n\n // ---- Channel lifecycle (abstract — every connector must implement) ----\n\n /**\n * Returns available channels for the authorized actor.\n * Called after OAuth is complete, during the setup/edit modal.\n *\n * @param auth - The completed authorization with provider and actor info\n * @param token - The access token for making API calls\n * @returns Promise resolving to available channels for the user to select\n */\n abstract getChannels(\n auth: Authorization | null,\n token: AuthToken | null\n ): Promise<Channel[]>;\n\n /**\n * Called when a channel resource is enabled for syncing.\n *\n * The framework dispatches this in three cases:\n * 1. **Initial enable** — user toggled the channel on for the first time.\n * 2. **Auto-enable** — `setChannels` discovered a new channel on a\n * connection with `auto_enable_new_channels` set.\n * 3. **Recovery after re-auth** — the user re-authorized a previously-\n * broken connection. The framework calls `onChannelEnabled` for every\n * channel that was already enabled at the time of re-auth, with\n * `context.recovering = true`. See {@link SyncContext.recovering}.\n *\n * Implementations should be **idempotent and overwrite stored state**:\n * the same channel may receive multiple `onChannelEnabled` calls across\n * its lifetime. Use unconditional `this.set()` writes rather than\n * coalesce/skip-if-present logic so a recovery dispatch wipes stale\n * cursors and state from the prior session.\n *\n * **Sync state tracking is automatic.** The framework stamps the\n * connection as \"syncing\" when it dispatches this method and clears\n * that state when:\n * - the connector calls `tools.integrations.channelSyncCompleted(id)`\n * once the initial backfill is done, OR\n * - this method throws an unhandled exception (auto-cleared so the UI\n * doesn't get stuck in \"syncing\" forever).\n *\n * **IMPORTANT: This method runs inline in the HTTP request handler.**\n * Any long-running work (webhook setup, API calls, sync) MUST be queued\n * as a separate task via `this.runTask()`, not executed inline. Blocking\n * here causes the client to spin waiting for the response.\n *\n * Only lightweight operations should appear directly in this method:\n * `this.set()`, `this.get()`, `this.callback()`, and `this.runTask()`.\n *\n * @example\n * ```typescript\n * async onChannelEnabled(channel: Channel, context?: SyncContext): Promise<void> {\n * // Recovery: drop stale cursors so the next sync re-walks history.\n * if (context?.recovering) {\n * await this.clear(`last_sync_token_${channel.id}`);\n * }\n *\n * await this.set(`sync_state_${channel.id}`, { channelId: channel.id });\n *\n * // Queue sync as a task — do NOT use this.run() or call sync methods inline\n * const syncCallback = await this.callback(this.syncBatch, 1, \"full\", channel.id, true);\n * await this.runTask(syncCallback);\n *\n * // Queue webhook setup as a task — do NOT call setupWebhook() inline\n * const webhookCallback = await this.callback(this.setupWebhook, channel.id);\n * await this.runTask(webhookCallback);\n * }\n * ```\n *\n * @param channel - The channel that was enabled\n * @param context - Optional sync context (plan-based hints, recovery flag)\n */\n abstract onChannelEnabled(channel: Channel, context?: SyncContext): Promise<void>;\n\n /**\n * Called when a channel resource is disabled.\n * Should stop sync, clean up webhooks, and remove state.\n *\n * @param channel - The channel that was disabled\n */\n abstract onChannelDisabled(channel: Channel): Promise<void>;\n\n // ---- Write-back hooks (optional, default no-ops) ----\n\n /**\n * Called when a link created by this connector is updated by the user.\n * Override to write back changes to the external service\n * (e.g., changing issue status in Linear when marked done in Plot).\n *\n * @param link - The updated link\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onLinkUpdated(link: Link): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a user creates a thread in Plot that should create a new\n * item in this connector's external system.\n *\n * A connector opts in to Plot-initiated creation by declaring a\n * `compose` block on the relevant `LinkTypeConfig` (see\n * {@link ComposeConfig}). When a user picks \"Create new <type>\" from the\n * Add link modal and the thread is synced, the runtime calls this method\n * with the draft fields.\n *\n * Implementations should create the item in the external service and\n * return a `NewLinkWithNotes` describing the created item. The platform\n * attaches the returned link to the originating thread — do not call\n * `integrations.saveLink` yourself.\n *\n * Returning `null` aborts creation silently (the thread is still saved\n * without a link).\n *\n * @param draft - The fields captured in Plot for the new item.\n * @returns The link to attach, or null to abort creation.\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onCreateLink(draft: CreateLinkDraft): Promise<NewLinkWithNotes | null> {\n return Promise.resolve(null);\n }\n\n /**\n * Called when a note is created on a thread owned by this connector.\n * Override to write back comments to the external service\n * (e.g., adding a comment to a Linear issue).\n *\n * Returning a string or {@link NoteWriteBackResult} links the Plot note\n * to its external counterpart. A plain string sets the note's `key`.\n * A `NoteWriteBackResult` additionally sets a sync baseline (via\n * `externalContent`) so the next sync-in can recognize the round-tripped\n * content and preserve Plot's formatted version. See\n * {@link NoteWriteBackResult} for details.\n *\n * @param note - The created note\n * @param thread - The thread the note belongs to (includes thread.meta with connector-specific data)\n * @returns Optional note key or NoteWriteBackResult for external dedup + baseline tracking\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onNoteCreated(note: Note, thread: Thread): Promise<string | NoteWriteBackResult | void> {\n return Promise.resolve();\n }\n\n /**\n * Resolve a `fileRef` action's bytes for download. Called when a user opens\n * an attachment in Plot. Return either a redirect URL (preferred for sources\n * that issue signed URLs, like Linear S3 or Slack permalink_public) or a\n * streamed body (required when bytes are only reachable through an\n * authenticated API call, like Gmail attachments.get).\n *\n * @param ref Opaque value the connector previously emitted on a fileRef action.\n * @returns Either `{ redirectUrl }` or `{ body, mimeType, fileName? }`.\n * @throws If the source is unavailable, the connection is broken, or `ref` is invalid.\n *\n * If not overridden, fileRef actions on this connector's notes will return 410 Gone.\n */\n async downloadAttachment(\n ref: string,\n ): Promise<\n | { redirectUrl: string }\n | { body: ReadableStream | Uint8Array; mimeType: string; fileName?: string }\n > {\n throw new Error(\n `downloadAttachment not implemented for ${this.constructor.name} (ref=${ref})`,\n );\n }\n\n /**\n * Called when a note on a thread owned by this connector is updated.\n * Override to write back changes to the external service\n * (e.g., syncing reaction tags as emoji reactions, or editing a comment\n * whose content changed in Plot).\n *\n * Return a {@link NoteWriteBackResult} with `externalContent` to update\n * the sync baseline after a successful write-back, so the next sync-in\n * recognizes the external version as already-seen and preserves Plot's\n * content.\n *\n * @param note - The updated note (includes current tags)\n * @param thread - The thread the note belongs to (includes thread.meta with connector-specific data)\n * @returns Optional NoteWriteBackResult for baseline tracking\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onNoteUpdated(note: Note, thread: Thread): Promise<NoteWriteBackResult | void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a user reads or unreads a thread owned by this connector.\n * Override to write back read status to the external service\n * (e.g., marking an email as read in Gmail).\n *\n * @param thread - The thread that was read/unread (includes thread.meta with connector-specific data)\n * @param actor - The user who performed the action\n * @param unread - false when marked as read, true when marked as unread\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onThreadRead(thread: Thread, actor: Actor, unread: boolean): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a user adds, removes, or changes the role of contacts on a\n * thread owned by this connector. Override on connectors whose source\n * supports mid-thread recipient changes (Gmail, IMAP, etc.). Connectors\n * that can't change recipients per-message (Slack, Linear) leave this as\n * the default no-op and should also declare\n * `LinkTypeConfig.supportsContactChanges: false`.\n *\n * The dispatch fires after Plot has persisted the change. Connectors are\n * expected to reflect it on the next outbound note (e.g. building To/Cc/Bcc\n * headers from the current `thread.contacts` × `thread.contactMeta`) — this\n * callback is not the right place to send a standalone notification.\n *\n * @param thread - The thread whose contacts changed\n * @param changes - The added/removed contacts and any role transitions on existing contacts\n */\n /* eslint-disable @typescript-eslint/no-unused-vars */\n onContactsChanged(\n thread: Thread,\n changes: {\n added: Array<{ contact: Contact; role: string }>;\n removed: Array<{ contact: Contact; role: string }>;\n changed: Array<{ contact: Contact; from: string; to: string }>;\n },\n ): Promise<void> {\n return Promise.resolve();\n }\n /* eslint-enable @typescript-eslint/no-unused-vars */\n\n /**\n * Called when a user marks or unmarks a thread as todo.\n * Override to sync todo status to the external service\n * (e.g., starring an email in Gmail when marked as todo).\n *\n * @param thread - The thread (includes thread.meta with connector-specific data)\n * @param actor - The user who changed the todo status\n * @param todo - true when marked as todo, false when done or removed\n * @param options - Additional context\n * @param options.date - The todo date (when todo=true)\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onThreadToDo(thread: Thread, actor: Actor, todo: boolean, options: { date?: Date }): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a schedule contact's RSVP status changes on a thread owned by this connector.\n * Override to sync RSVP changes back to the external calendar.\n *\n * @param thread - The thread (includes thread.meta with connector-specific data)\n * @param scheduleId - The schedule ID\n * @param contactId - The contact whose status changed\n * @param status - The new RSVP status ('attend', 'skip', or null)\n * @param actor - The user who changed the status\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onScheduleContactUpdated(thread: Thread, scheduleId: string, contactId: ActorId, status: ScheduleContactStatus | null, actor: Actor): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a user adds or removes a single emoji reaction on a note\n * (one event per `(note, actor, emoji)` state transition).\n *\n * Dispatch is routed to the reacting user's own connector instance via\n * `twist_instance_for_actor` on `note_reaction.actor_id`, so this method\n * already runs under the reactor's auth. Fetch the API client with the\n * connector's normal token-fetch path (`this.tools.integrations.get(...)`)\n * and the external write — e.g. Slack `reactions.add` — will be attributed\n * to the correct user. No `actAs` step required.\n *\n * If the reacting user has no connection of this type, no dispatch fires\n * for that reaction (it stays in Plot only).\n *\n * Override to sync per-actor reactions back to the external system.\n *\n * @param note - The note that was reacted on (partial; `id`, `key`, `content` populated)\n * @param thread - The thread the note belongs to (partial; `id`, `title`, `archived`, `meta` populated)\n * @param actor - The contact who added/removed the reaction\n * @param emoji - The emoji (Unicode grapheme or `provider:workspace/name` custom-emoji ref)\n * @param added - `true` if the reaction is now present, `false` if it was removed\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onNoteReactionChanged(note: Note, thread: Thread, actor: Actor, emoji: string, added: boolean): Promise<void> {\n return Promise.resolve();\n }\n\n // ---- Activation ----\n\n /**\n * Called when the connector is activated after OAuth is complete.\n *\n * Connectors receive the authorization in addition to the activating actor.\n * When this runs, `this.userId` is already populated with the installing\n * user's ID.\n *\n * Default implementation does nothing. Override for custom setup.\n *\n * @param context - The activation context\n * @param context.auth - The completed OAuth authorization\n * @param context.actor - The actor who activated the connector\n */\n // @ts-ignore - Connector.activate() has a Connector-specific context type.\n activate(context: { auth?: Authorization; actor?: Actor }): Promise<void> {\n return Promise.resolve();\n }\n}\n\n/** @deprecated Use `Connector` instead. */\nexport { Connector as Source };\n";
|