@plotday/twister 0.61.0 → 0.63.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 (68) hide show
  1. package/dist/connector.d.ts +14 -0
  2. package/dist/connector.d.ts.map +1 -1
  3. package/dist/connector.js +14 -0
  4. package/dist/connector.js.map +1 -1
  5. package/dist/docs/assets/hierarchy.js +1 -1
  6. package/dist/docs/assets/search.js +1 -1
  7. package/dist/docs/classes/index.Connector.html +47 -26
  8. package/dist/docs/classes/index.FileNotFoundError.html +1 -1
  9. package/dist/docs/classes/index.Files.html +1 -1
  10. package/dist/docs/classes/index.Imap.html +1 -1
  11. package/dist/docs/classes/index.Options.html +1 -1
  12. package/dist/docs/classes/index.Smtp.html +1 -1
  13. package/dist/docs/classes/tool.ITool.html +1 -1
  14. package/dist/docs/classes/tool.Tool.html +17 -7
  15. package/dist/docs/classes/tools_ai.AI.html +1 -1
  16. package/dist/docs/classes/tools_callbacks.Callbacks.html +1 -1
  17. package/dist/docs/classes/tools_integrations.Integrations.html +1 -1
  18. package/dist/docs/classes/tools_network.Network.html +1 -1
  19. package/dist/docs/classes/tools_plot.Plot.html +1 -1
  20. package/dist/docs/classes/tools_store.Store.html +1 -1
  21. package/dist/docs/classes/tools_tasks.Tasks.html +40 -14
  22. package/dist/docs/classes/tools_twists.Twists.html +1 -1
  23. package/dist/docs/classes/twist.Twist.html +21 -11
  24. package/dist/docs/documents/Built-in_Tools.html +15 -13
  25. package/dist/docs/documents/Runtime_Environment.html +7 -5
  26. package/dist/docs/hierarchy.html +1 -1
  27. package/dist/docs/media/AGENTS.md +23 -19
  28. package/dist/llm-docs/connector.d.ts +1 -1
  29. package/dist/llm-docs/connector.d.ts.map +1 -1
  30. package/dist/llm-docs/connector.js +1 -1
  31. package/dist/llm-docs/connector.js.map +1 -1
  32. package/dist/llm-docs/tool.d.ts +1 -1
  33. package/dist/llm-docs/tool.d.ts.map +1 -1
  34. package/dist/llm-docs/tool.js +1 -1
  35. package/dist/llm-docs/tool.js.map +1 -1
  36. package/dist/llm-docs/tools/tasks.d.ts +1 -1
  37. package/dist/llm-docs/tools/tasks.d.ts.map +1 -1
  38. package/dist/llm-docs/tools/tasks.js +1 -1
  39. package/dist/llm-docs/tools/tasks.js.map +1 -1
  40. package/dist/llm-docs/twist.d.ts +1 -1
  41. package/dist/llm-docs/twist.d.ts.map +1 -1
  42. package/dist/llm-docs/twist.js +1 -1
  43. package/dist/llm-docs/twist.js.map +1 -1
  44. package/dist/tool.d.ts +16 -0
  45. package/dist/tool.d.ts.map +1 -1
  46. package/dist/tool.js +15 -0
  47. package/dist/tool.js.map +1 -1
  48. package/dist/tools/tasks.d.ts +58 -12
  49. package/dist/tools/tasks.d.ts.map +1 -1
  50. package/dist/tools/tasks.js.map +1 -1
  51. package/dist/twist.d.ts +16 -0
  52. package/dist/twist.d.ts.map +1 -1
  53. package/dist/twist.js +15 -0
  54. package/dist/twist.js.map +1 -1
  55. package/dist/utils/markdown-html.d.ts +15 -0
  56. package/dist/utils/markdown-html.d.ts.map +1 -0
  57. package/dist/utils/markdown-html.js +35 -0
  58. package/dist/utils/markdown-html.js.map +1 -0
  59. package/package.json +7 -1
  60. package/src/connector.ts +15 -0
  61. package/src/llm-docs/connector.ts +1 -1
  62. package/src/llm-docs/tool.ts +1 -1
  63. package/src/llm-docs/tools/tasks.ts +1 -1
  64. package/src/llm-docs/twist.ts +1 -1
  65. package/src/tool.ts +20 -0
  66. package/src/tools/tasks.ts +61 -12
  67. package/src/twist.ts +20 -0
  68. package/src/utils/markdown-html.ts +40 -0
@@ -4,5 +4,5 @@
4
4
  * This file is auto-generated during build. Do not edit manually.
5
5
  * Generated from: prebuild.ts
6
6
  */
7
- export default "import {\n type Actor,\n} from \"./plot\";\nimport type { Callback } from \"./tools/callbacks\";\nimport type {\n InferOptions,\n InferTools,\n Serializable,\n ToolBuilder,\n ToolShed,\n} from \"./utils/types\";\n\nexport type { ToolBuilder };\n\n/**\n * Abstract parent for both built-in tools and regular Tools.\n * Regular tools extend Tool.\n */\nexport abstract class ITool {}\n\n/**\n * Base class for regular tools.\n *\n * Regular tools run in isolation and can only access other tools declared\n * in their build method. They are ideal for external API integrations\n * and reusable functionality that doesn't require Plot's internal infrastructure.\n *\n * @example\n * ```typescript\n * class GoogleCalendarTool extends Tool<GoogleCalendarTool> {\n * constructor(id: string, options: { clientId: string }) {\n * super(id, options);\n * }\n *\n * build(tools: ToolBuilder) {\n * return {\n * auth: tools.build(Integrations),\n * network: tools.build(Network),\n * };\n * }\n *\n * async getCalendars() {\n * const token = await this.tools.auth.get(...);\n * // Implementation\n * }\n * }\n * ```\n */\nexport abstract class Tool<TSelf> implements ITool {\n constructor(\n protected id: string,\n protected options: InferOptions<TSelf>,\n private toolShed: ToolShed\n ) {}\n\n /**\n * Gets the initialized tools for this tool.\n * @throws Error if called before initialization is complete\n */\n protected get tools() {\n return this.toolShed.getTools<InferTools<TSelf>>();\n }\n\n /**\n * Declares tool dependencies for this tool.\n * Return an object mapping tool names to build() promises.\n * Default implementation returns empty object (no custom tools).\n *\n * @param build - The build function to use for declaring dependencies\n * @returns Object mapping tool names to tool promises\n *\n * @example\n * ```typescript\n * build(build: ToolBuilder) {\n * return {\n * network: build(Network, { urls: [\"https://api.example.com/*\"] }),\n * };\n * }\n * ```\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n build(build: ToolBuilder): Record<string, Promise<ITool>> {\n return {};\n }\n\n /**\n * Creates a persistent callback to a method on this tool.\n *\n * ExtraArgs are strongly typed to match the method's signature.\n *\n * @param fn - The method to callback\n * @param extraArgs - Additional arguments to pass (type-checked, must be serializable)\n * @returns Promise resolving to a persistent callback token\n *\n * @example\n * ```typescript\n * const callback = await this.callback(this.onWebhook, \"calendar\", 123);\n * ```\n */\n protected async callback<\n TArgs extends Serializable[],\n Fn extends (...args: TArgs) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback> {\n return this.tools.callbacks.create(fn, ...extraArgs);\n }\n\n /**\n * Deletes a specific callback by its token.\n *\n * @param token - The callback token to delete\n * @returns Promise that resolves when the callback is deleted\n */\n protected async deleteCallback(token: Callback): Promise<void> {\n return this.tools.callbacks.delete(token);\n }\n\n /**\n * Deletes all callbacks for this tool.\n *\n * @returns Promise that resolves when all callbacks are deleted\n */\n protected async deleteAllCallbacks(): Promise<void> {\n return this.tools.callbacks.deleteAll();\n }\n\n /**\n * Executes a callback by its token inline in the current execution.\n *\n * **Use `this.runTask()` instead for batch continuations and long-running work.**\n * `this.run()` executes inline, sharing the current request count (~1000 limit)\n * and blocking the HTTP response. This causes timeouts when used in lifecycle\n * methods like `onChannelEnabled` or `syncBatch` continuations.\n *\n * `this.run()` is appropriate when you need the callback's **return value** —\n * e.g., running a parent callback token that returns data. For fire-and-forget\n * work, always prefer `this.runTask()`.\n *\n * @param token - The callback token to execute\n * @param args - Optional arguments to pass to the callback\n * @returns Promise resolving to the callback result\n */\n protected async run(token: Callback, ...args: any[]): Promise<any> {\n return this.tools.callbacks.run(token, ...args);\n }\n\n /**\n * Retrieves a value from persistent storage by key.\n *\n * Values are automatically deserialized using SuperJSON, which\n * properly restores Date objects, Maps, Sets, and other complex types.\n *\n * @template T - The expected type of the stored value (must be Serializable)\n * @param key - The storage key to retrieve\n * @returns Promise resolving to the stored value or null\n */\n protected async get<T extends Serializable>(key: string): Promise<T | null> {\n return this.tools.store.get(key);\n }\n\n /**\n * Stores a value in persistent storage.\n *\n * The value will be serialized using SuperJSON and stored persistently.\n * SuperJSON automatically handles Date objects, Maps, Sets, undefined values,\n * and other complex types that standard JSON doesn't support.\n *\n * **Important**: Functions and Symbols cannot be stored.\n * **For function references**: Use callbacks instead of storing functions directly.\n *\n * @example\n * ```typescript\n * // ✅ Date objects are preserved\n * await this.set(\"sync_state\", {\n * lastSync: new Date(),\n * minDate: new Date(2024, 0, 1)\n * });\n *\n * // ✅ undefined is now supported\n * await this.set(\"data\", { name: \"test\", optional: undefined });\n *\n * // ✅ Arrays with undefined are supported\n * await this.set(\"items\", [1, undefined, 3]);\n * await this.set(\"items\", [1, null, 3]); // Also works\n *\n * // ✅ Maps and Sets are supported\n * await this.set(\"mapping\", new Map([[\"key\", \"value\"]]));\n * await this.set(\"tags\", new Set([\"tag1\", \"tag2\"]));\n *\n * // ❌ WRONG: Cannot store functions directly\n * await this.set(\"handler\", this.myHandler);\n *\n * // ✅ CORRECT: Create a callback token first\n * const token = await this.callback(this.myHandler, \"arg1\", \"arg2\");\n * await this.set(\"handler_token\", token);\n *\n * // Later, execute the callback\n * const token = await this.get<Callback>(\"handler_token\");\n * await this.run(token);\n * ```\n *\n * @template T - The type of value being stored (must be Serializable)\n * @param key - The storage key to use\n * @param value - The value to store (must be SuperJSON-serializable)\n * @returns Promise that resolves when the value is stored\n */\n protected async set<T extends Serializable>(\n key: string,\n value: T\n ): Promise<void> {\n return this.tools.store.set(key, value);\n }\n\n /**\n * Lists all storage keys matching a prefix.\n *\n * @param prefix - The prefix to match keys against\n * @returns Promise resolving to an array of matching key strings\n */\n protected async list(prefix: string): Promise<string[]> {\n return this.tools.store.list(prefix);\n }\n\n /**\n * Removes a specific key from persistent storage.\n *\n * @param key - The storage key to remove\n * @returns Promise that resolves when the key is removed\n */\n protected async clear(key: string): Promise<void> {\n return this.tools.store.clear(key);\n }\n\n /**\n * Removes all keys from this tool's storage.\n *\n * @returns Promise that resolves when all keys are removed\n */\n protected async clearAll(): Promise<void> {\n return this.tools.store.clearAll();\n }\n\n /**\n * Queues a callback to execute in a separate worker context with a fresh request limit.\n *\n * **Creates a NEW execution** with its own request limit of ~1000 requests (HTTP requests,\n * tool calls, database operations). This is the primary way to stay under request limits\n * when processing large datasets or making many API calls.\n *\n * Use this to break long loops into chunks that each stay under the ~1000 request limit.\n * Each task runs in an isolated execution environment with ~1000 requests and ~60 seconds CPU time.\n *\n * @param callback - The callback token created with `this.callback()`\n * @param options - Optional configuration for the execution\n * @param options.runAt - If provided, schedules execution at this time; otherwise runs immediately\n * @returns Promise resolving to a cancellation token (only for scheduled executions)\n *\n * @example\n * ```typescript\n * // Break large loop into batches\n * const callback = await this.callback(this.processBatch, 1);\n * await this.runTask(callback); // New execution with fresh request limit\n * ```\n */\n protected async runTask(\n callback: Callback,\n options?: { runAt?: Date }\n ): Promise<string | void> {\n return this.tools.tasks.runTask(callback, options);\n }\n\n /**\n * Cancels a previously scheduled execution.\n *\n * @param token - The cancellation token returned by runTask() with runAt option\n * @returns Promise that resolves when the cancellation is processed\n */\n protected async cancelTask(token: string): Promise<void> {\n return this.tools.tasks.cancelTask(token);\n }\n\n /**\n * Cancels all scheduled executions for this tool.\n *\n * @returns Promise that resolves when all cancellations are processed\n */\n protected async cancelAllTasks(): Promise<void> {\n return this.tools.tasks.cancelAllTasks();\n }\n\n /**\n * Schedules a **singleton** task keyed by `key`: re-scheduling under the same\n * key atomically replaces any pending task, so at most one is ever live.\n *\n * Prefer this over `runTask({ runAt })` for recurring/self-renewing jobs\n * (watch renewals, polling, deferred cleanup) — it removes the error-prone\n * \"store token, cancel before re-scheduling\" bookkeeping that otherwise leaks\n * parallel task chains. See {@link Tasks.scheduleTask}.\n *\n * @param key - Stable identifier scoped to what the task renews\n * @param callback - The callback token created with `this.callback()`\n * @param options.runAt - When to run (required)\n * @returns Promise resolving to the scheduled task's cancellation token\n */\n protected async scheduleTask(\n key: string,\n callback: Callback,\n options: { runAt: Date }\n ): Promise<string | void> {\n return this.tools.tasks.scheduleTask(key, callback, options);\n }\n\n /**\n * Cancels the singleton task previously scheduled under `key` (if any).\n * No-op if none exists or it already ran. See {@link Tasks.cancelScheduledTask}.\n *\n * @param key - The same key passed to {@link scheduleTask}\n * @returns Promise that resolves when the cancellation is processed\n */\n protected async cancelScheduledTask(key: string): Promise<void> {\n return this.tools.tasks.cancelScheduledTask(key);\n }\n\n /**\n * Called before the twist's activate method, starting from the deepest tool dependencies.\n *\n * This method is called in a depth-first manner, with the deepest dependencies\n * being called first, bubbling up to the top-level tools before the twist's\n * activate method is called.\n *\n * @param context - Optional context containing the actor who triggered activation\n * @returns Promise that resolves when pre-activation is complete\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n preActivate(context?: { actor: Actor }): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called after the twist's activate method, starting from the top-level tools.\n *\n * This method is called in reverse order, with top-level tools being called\n * first, then cascading down to the deepest dependencies.\n *\n * @param context - Optional context containing the actor who triggered activation\n * @returns Promise that resolves when post-activation is complete\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n postActivate(context?: { actor: Actor }): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called before the twist's upgrade method, starting from the deepest tool dependencies.\n *\n * This method is called in a depth-first manner, with the deepest dependencies\n * being called first, bubbling up to the top-level tools before the twist's\n * upgrade method is called.\n *\n * @returns Promise that resolves when pre-upgrade is complete\n */\n preUpgrade(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called after the twist's upgrade method, starting from the top-level tools.\n *\n * This method is called in reverse order, with top-level tools being called\n * first, then cascading down to the deepest dependencies.\n *\n * @returns Promise that resolves when post-upgrade is complete\n */\n postUpgrade(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called before the twist's deactivate method, starting from the deepest tool dependencies.\n *\n * This method is called in a depth-first manner, with the deepest dependencies\n * being called first, bubbling up to the top-level tools before the twist's\n * deactivate method is called.\n *\n * @returns Promise that resolves when pre-deactivation is complete\n */\n preDeactivate(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called after the twist's deactivate method, starting from the top-level tools.\n *\n * This method is called in reverse order, with top-level tools being called\n * first, then cascading down to the deepest dependencies.\n *\n * @returns Promise that resolves when post-deactivation is complete\n */\n postDeactivate(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Waits for tool initialization to complete.\n * Called automatically by the entrypoint before lifecycle methods.\n * @internal\n */\n async waitForReady(): Promise<void> {\n await this.toolShed.waitForReady();\n }\n}\n";
7
+ export default "import {\n type Actor,\n} from \"./plot\";\nimport type { Callback } from \"./tools/callbacks\";\nimport type {\n InferOptions,\n InferTools,\n Serializable,\n ToolBuilder,\n ToolShed,\n} from \"./utils/types\";\n\nexport type { ToolBuilder };\n\n/**\n * Abstract parent for both built-in tools and regular Tools.\n * Regular tools extend Tool.\n */\nexport abstract class ITool {}\n\n/**\n * Base class for regular tools.\n *\n * Regular tools run in isolation and can only access other tools declared\n * in their build method. They are ideal for external API integrations\n * and reusable functionality that doesn't require Plot's internal infrastructure.\n *\n * @example\n * ```typescript\n * class GoogleCalendarTool extends Tool<GoogleCalendarTool> {\n * constructor(id: string, options: { clientId: string }) {\n * super(id, options);\n * }\n *\n * build(tools: ToolBuilder) {\n * return {\n * auth: tools.build(Integrations),\n * network: tools.build(Network),\n * };\n * }\n *\n * async getCalendars() {\n * const token = await this.tools.auth.get(...);\n * // Implementation\n * }\n * }\n * ```\n */\nexport abstract class Tool<TSelf> implements ITool {\n constructor(\n protected id: string,\n protected options: InferOptions<TSelf>,\n private toolShed: ToolShed\n ) {}\n\n /**\n * Gets the initialized tools for this tool.\n * @throws Error if called before initialization is complete\n */\n protected get tools() {\n return this.toolShed.getTools<InferTools<TSelf>>();\n }\n\n /**\n * Declares tool dependencies for this tool.\n * Return an object mapping tool names to build() promises.\n * Default implementation returns empty object (no custom tools).\n *\n * @param build - The build function to use for declaring dependencies\n * @returns Object mapping tool names to tool promises\n *\n * @example\n * ```typescript\n * build(build: ToolBuilder) {\n * return {\n * network: build(Network, { urls: [\"https://api.example.com/*\"] }),\n * };\n * }\n * ```\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n build(build: ToolBuilder): Record<string, Promise<ITool>> {\n return {};\n }\n\n /**\n * Creates a persistent callback to a method on this tool.\n *\n * ExtraArgs are strongly typed to match the method's signature.\n *\n * @param fn - The method to callback\n * @param extraArgs - Additional arguments to pass (type-checked, must be serializable)\n * @returns Promise resolving to a persistent callback token\n *\n * @example\n * ```typescript\n * const callback = await this.callback(this.onWebhook, \"calendar\", 123);\n * ```\n */\n protected async callback<\n TArgs extends Serializable[],\n Fn extends (...args: TArgs) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback> {\n return this.tools.callbacks.create(fn, ...extraArgs);\n }\n\n /**\n * Deletes a specific callback by its token.\n *\n * @param token - The callback token to delete\n * @returns Promise that resolves when the callback is deleted\n */\n protected async deleteCallback(token: Callback): Promise<void> {\n return this.tools.callbacks.delete(token);\n }\n\n /**\n * Deletes all callbacks for this tool.\n *\n * @returns Promise that resolves when all callbacks are deleted\n */\n protected async deleteAllCallbacks(): Promise<void> {\n return this.tools.callbacks.deleteAll();\n }\n\n /**\n * Executes a callback by its token inline in the current execution.\n *\n * **Use `this.runTask()` instead for batch continuations and long-running work.**\n * `this.run()` executes inline, sharing the current request count (~1000 limit)\n * and blocking the HTTP response. This causes timeouts when used in lifecycle\n * methods like `onChannelEnabled` or `syncBatch` continuations.\n *\n * `this.run()` is appropriate when you need the callback's **return value** —\n * e.g., running a parent callback token that returns data. For fire-and-forget\n * work, always prefer `this.runTask()`.\n *\n * @param token - The callback token to execute\n * @param args - Optional arguments to pass to the callback\n * @returns Promise resolving to the callback result\n */\n protected async run(token: Callback, ...args: any[]): Promise<any> {\n return this.tools.callbacks.run(token, ...args);\n }\n\n /**\n * Retrieves a value from persistent storage by key.\n *\n * Values are automatically deserialized using SuperJSON, which\n * properly restores Date objects, Maps, Sets, and other complex types.\n *\n * @template T - The expected type of the stored value (must be Serializable)\n * @param key - The storage key to retrieve\n * @returns Promise resolving to the stored value or null\n */\n protected async get<T extends Serializable>(key: string): Promise<T | null> {\n return this.tools.store.get(key);\n }\n\n /**\n * Stores a value in persistent storage.\n *\n * The value will be serialized using SuperJSON and stored persistently.\n * SuperJSON automatically handles Date objects, Maps, Sets, undefined values,\n * and other complex types that standard JSON doesn't support.\n *\n * **Important**: Functions and Symbols cannot be stored.\n * **For function references**: Use callbacks instead of storing functions directly.\n *\n * @example\n * ```typescript\n * // ✅ Date objects are preserved\n * await this.set(\"sync_state\", {\n * lastSync: new Date(),\n * minDate: new Date(2024, 0, 1)\n * });\n *\n * // ✅ undefined is now supported\n * await this.set(\"data\", { name: \"test\", optional: undefined });\n *\n * // ✅ Arrays with undefined are supported\n * await this.set(\"items\", [1, undefined, 3]);\n * await this.set(\"items\", [1, null, 3]); // Also works\n *\n * // ✅ Maps and Sets are supported\n * await this.set(\"mapping\", new Map([[\"key\", \"value\"]]));\n * await this.set(\"tags\", new Set([\"tag1\", \"tag2\"]));\n *\n * // ❌ WRONG: Cannot store functions directly\n * await this.set(\"handler\", this.myHandler);\n *\n * // ✅ CORRECT: Create a callback token first\n * const token = await this.callback(this.myHandler, \"arg1\", \"arg2\");\n * await this.set(\"handler_token\", token);\n *\n * // Later, execute the callback\n * const token = await this.get<Callback>(\"handler_token\");\n * await this.run(token);\n * ```\n *\n * @template T - The type of value being stored (must be Serializable)\n * @param key - The storage key to use\n * @param value - The value to store (must be SuperJSON-serializable)\n * @returns Promise that resolves when the value is stored\n */\n protected async set<T extends Serializable>(\n key: string,\n value: T\n ): Promise<void> {\n return this.tools.store.set(key, value);\n }\n\n /**\n * Lists all storage keys matching a prefix.\n *\n * @param prefix - The prefix to match keys against\n * @returns Promise resolving to an array of matching key strings\n */\n protected async list(prefix: string): Promise<string[]> {\n return this.tools.store.list(prefix);\n }\n\n /**\n * Removes a specific key from persistent storage.\n *\n * @param key - The storage key to remove\n * @returns Promise that resolves when the key is removed\n */\n protected async clear(key: string): Promise<void> {\n return this.tools.store.clear(key);\n }\n\n /**\n * Removes all keys from this tool's storage.\n *\n * @returns Promise that resolves when all keys are removed\n */\n protected async clearAll(): Promise<void> {\n return this.tools.store.clearAll();\n }\n\n /**\n * Queues a callback to execute in a separate worker context with a fresh request limit.\n *\n * **Creates a NEW execution** with its own request limit of ~1000 requests (HTTP requests,\n * tool calls, database operations). This is the primary way to stay under request limits\n * when processing large datasets or making many API calls.\n *\n * Use this to break long loops into chunks that each stay under the ~1000 request limit.\n * Each task runs in an isolated execution environment with ~1000 requests and ~60 seconds CPU time.\n *\n * @param callback - The callback token created with `this.callback()`\n * @param options - Optional configuration for the execution\n * @param options.runAt - If provided, schedules execution at this time; otherwise runs immediately\n * @returns Promise resolving to a cancellation token (only for scheduled executions)\n *\n * @example\n * ```typescript\n * // Break large loop into batches\n * const callback = await this.callback(this.processBatch, 1);\n * await this.runTask(callback); // New execution with fresh request limit\n * ```\n */\n protected async runTask(\n callback: Callback,\n options?: { runAt?: Date }\n ): Promise<string | void> {\n return this.tools.tasks.runTask(callback, options);\n }\n\n /**\n * Cancels a previously scheduled execution.\n *\n * @param token - The cancellation token returned by runTask() with runAt option\n * @returns Promise that resolves when the cancellation is processed\n */\n protected async cancelTask(token: string): Promise<void> {\n return this.tools.tasks.cancelTask(token);\n }\n\n /**\n * Cancels all scheduled executions for this tool.\n *\n * @returns Promise that resolves when all cancellations are processed\n */\n protected async cancelAllTasks(): Promise<void> {\n return this.tools.tasks.cancelAllTasks();\n }\n\n /**\n * Schedules a **singleton** task keyed by `key`: re-scheduling under the same\n * key atomically replaces any pending task, so at most one is ever live.\n *\n * Prefer this over `runTask({ runAt })` for recurring/self-renewing jobs\n * (watch renewals, polling, deferred cleanup) — it removes the error-prone\n * \"store token, cancel before re-scheduling\" bookkeeping that otherwise leaks\n * parallel task chains. See {@link Tasks.scheduleTask}.\n *\n * @param key - Stable identifier scoped to what the task renews\n * @param callback - The callback token created with `this.callback()`\n * @param options.runAt - When to run (required)\n * @returns Promise resolving to the scheduled task's cancellation token\n */\n protected async scheduleTask(\n key: string,\n callback: Callback,\n options: { runAt: Date }\n ): Promise<string | void> {\n return this.tools.tasks.scheduleTask(key, callback, options);\n }\n\n /**\n * Cancels the singleton task previously scheduled under `key` (if any).\n * No-op if none exists or it already ran. See {@link Tasks.cancelScheduledTask}.\n *\n * @param key - The same key passed to {@link scheduleTask}\n * @returns Promise that resolves when the cancellation is processed\n */\n protected async cancelScheduledTask(key: string): Promise<void> {\n return this.tools.tasks.cancelScheduledTask(key);\n }\n\n /**\n * Schedules a durable recurring task under a stable key. The platform\n * re-arms the task every `intervalMs` automatically — the callback does NOT\n * need to reschedule itself. Re-scheduling under the same key atomically\n * replaces the pending occurrence (at most one live task per key). Tear down\n * with {@link cancelScheduledTask}. See {@link Tasks.scheduleRecurring}.\n *\n * @param key - Stable identifier, e.g. `\"mailbox-self-heal\"`\n * @param callback - Callback token created with `this.callback()`\n * @param options.intervalMs - Safety-ceiling cadence in milliseconds\n * @param options.firstRunAt - Optional precise time for the next fire\n */\n protected async scheduleRecurring(\n key: string,\n callback: Callback,\n options: { intervalMs: number; firstRunAt?: Date }\n ): Promise<void> {\n return this.tools.tasks.scheduleRecurring(key, callback, options);\n }\n\n /**\n * Called before the twist's activate method, starting from the deepest tool dependencies.\n *\n * This method is called in a depth-first manner, with the deepest dependencies\n * being called first, bubbling up to the top-level tools before the twist's\n * activate method is called.\n *\n * @param context - Optional context containing the actor who triggered activation\n * @returns Promise that resolves when pre-activation is complete\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n preActivate(context?: { actor: Actor }): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called after the twist's activate method, starting from the top-level tools.\n *\n * This method is called in reverse order, with top-level tools being called\n * first, then cascading down to the deepest dependencies.\n *\n * @param context - Optional context containing the actor who triggered activation\n * @returns Promise that resolves when post-activation is complete\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n postActivate(context?: { actor: Actor }): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called before the twist's upgrade method, starting from the deepest tool dependencies.\n *\n * This method is called in a depth-first manner, with the deepest dependencies\n * being called first, bubbling up to the top-level tools before the twist's\n * upgrade method is called.\n *\n * @returns Promise that resolves when pre-upgrade is complete\n */\n preUpgrade(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called after the twist's upgrade method, starting from the top-level tools.\n *\n * This method is called in reverse order, with top-level tools being called\n * first, then cascading down to the deepest dependencies.\n *\n * @returns Promise that resolves when post-upgrade is complete\n */\n postUpgrade(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called before the twist's deactivate method, starting from the deepest tool dependencies.\n *\n * This method is called in a depth-first manner, with the deepest dependencies\n * being called first, bubbling up to the top-level tools before the twist's\n * deactivate method is called.\n *\n * @returns Promise that resolves when pre-deactivation is complete\n */\n preDeactivate(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called after the twist's deactivate method, starting from the top-level tools.\n *\n * This method is called in reverse order, with top-level tools being called\n * first, then cascading down to the deepest dependencies.\n *\n * @returns Promise that resolves when post-deactivation is complete\n */\n postDeactivate(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Waits for tool initialization to complete.\n * Called automatically by the entrypoint before lifecycle methods.\n * @internal\n */\n async waitForReady(): Promise<void> {\n await this.toolShed.waitForReady();\n }\n}\n";
8
8
  //# sourceMappingURL=tool.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/llm-docs/tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAe,m4bAAm4b,CAAC"}
1
+ {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/llm-docs/tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAe,sydAAsyd,CAAC"}
@@ -4,6 +4,6 @@
4
4
  * This file is auto-generated during build. Do not edit manually.
5
5
  * Generated from: prebuild.ts
6
6
  */
7
- declare const _default: "import { ITool } from \"..\";\nimport type { Callback } from \"./callbacks\";\n\n/**\n * Run background tasks and scheduled jobs.\n *\n * The Tasks tool enables twists and tools to queue callbacks for execution in separate\n * worker contexts. **This is critical for staying under request limits**: each execution\n * has a limit of ~1000 requests (HTTP requests, tool calls, database operations), and\n * running a task creates a NEW execution with a fresh request limit.\n *\n * **Key distinction:**\n * - **Calling a callback** (via `this.run()`) continues the same execution and shares the request count\n * - **Running a task** (via `this.runTask()`) creates a NEW execution with fresh ~1000 request limit\n *\n * **When to use tasks:**\n * - Processing large datasets that would exceed 1000 requests\n * - Breaking loops into chunks where each chunk stays under the request limit\n * - Scheduling operations for future execution\n *\n * **Note:** Tasks tool methods are also available directly on Twist and Tool classes\n * via `this.runTask()`, `this.cancelTask()`, and `this.cancelAllTasks()`.\n * This is the recommended approach for most use cases.\n *\n * **Best Practices:**\n * - Size batches to stay under ~1000 requests per execution\n * - Calculate requests per item to determine safe batch size\n * - Create callbacks first using `this.callback()`\n * - Store intermediate state using the Store tool\n *\n * @example\n * ```typescript\n * class SyncTool extends Tool<SyncTool> {\n * async startBatchSync(totalItems: number) {\n * // Store initial state using built-in set method\n * await this.set(\"sync_progress\", { processed: 0, total: totalItems });\n *\n * // Create callback and queue first batch\n * const callback = await this.callback(this.processBatch, 1);\n * // runTask creates NEW execution with fresh ~1000 request limit\n * await this.runTask(callback);\n * }\n *\n * async processBatch(batchNumber: number) {\n * // Process one batch of items (sized to stay under request limit)\n * const progress = await this.get(\"sync_progress\");\n *\n * // If each item makes ~10 requests, process ~100 items per batch\n * // 100 items \u00D7 10 requests = 1000 requests (at limit)\n * const batchSize = 100;\n * const items = await this.fetchItems(progress.processed, batchSize);\n *\n * for (const item of items) {\n * await this.processItem(item); // Makes ~10 requests per item\n * }\n *\n * await this.set(\"sync_progress\", {\n * processed: progress.processed + batchSize,\n * total: progress.total\n * });\n *\n * if (progress.processed < progress.total) {\n * // Queue next batch - creates NEW execution with fresh request limit\n * const callback = await this.callback(this.processBatch, batchNumber + 1);\n * await this.runTask(callback);\n * }\n * }\n *\n * async scheduleCleanup() {\n * const tomorrow = new Date();\n * tomorrow.setDate(tomorrow.getDate() + 1);\n *\n * const callback = await this.callback(this.cleanupOldData);\n * // Schedule for future execution\n * return await this.runTask(callback, { runAt: tomorrow });\n * }\n * }\n * ```\n */\nexport abstract class Tasks extends ITool {\n /**\n * Queues a callback to execute in a separate worker context with a fresh request limit.\n *\n * **Creates a NEW execution** with its own request limit of ~1000 requests (HTTP requests,\n * tool calls, database operations). This is the primary way to stay under request limits\n * when processing large datasets or making many API calls.\n *\n * The callback will be invoked either immediately or at a scheduled time\n * in an isolated execution environment. Each execution has ~1000 requests and ~60 seconds\n * CPU time. Use this for breaking loops into chunks that stay under the request limit.\n *\n * **Key distinction:**\n * - `this.run(callback)` - Continues same execution, shares request count\n * - `this.runTask(callback)` - NEW execution, fresh request limit\n *\n * @param callback - Callback created with `this.callback()`\n * @param options - Optional configuration for the execution\n * @param options.runAt - If provided, schedules execution at this time; otherwise runs immediately\n * @returns Promise resolving to a cancellation token (only for scheduled executions)\n *\n * @example\n * ```typescript\n * // Break large loop into batches to stay under request limit\n * const callback = await this.callback(this.syncBatch, 1);\n * await this.runTask(callback); // Fresh execution with ~1000 requests\n * ```\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract runTask(\n callback: Callback,\n options?: { runAt?: Date }\n ): Promise<string | void>;\n\n /**\n * Cancels a previously scheduled execution.\n *\n * Prevents a scheduled function from executing. No error is thrown\n * if the token is invalid or the execution has already completed.\n *\n * @param token - The cancellation token returned by runTask() with runAt option\n * @returns Promise that resolves when the cancellation is processed\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract cancelTask(token: string): Promise<void>;\n\n /**\n * Cancels all scheduled executions for this tool/twist.\n *\n * Cancels all pending scheduled executions created by this tool or twist\n * instance. Immediate executions cannot be cancelled.\n *\n * @returns Promise that resolves when all cancellations are processed\n */\n abstract cancelAllTasks(): Promise<void>;\n\n /**\n * Schedules a **singleton** task identified by `key`: scheduling under a key\n * that already has a pending task atomically cancels the existing one and\n * replaces it. At most one scheduled task per `key` is ever live.\n *\n * Use this for any recurring/self-renewing job \u2014 webhook/watch renewals,\n * periodic polling, deferred cleanup \u2014 instead of hand-managing tokens with\n * `runTask()` + `cancelTask()`. The manual pattern (store the token, cancel\n * it before re-scheduling) is easy to get wrong: a renewal callback that\n * re-schedules itself, combined with any *extra* scheduling call (a\n * re-dispatched `onChannelEnabled`, a re-init), leaks parallel self-\n * perpetuating chains that accumulate forever and can trip the runtime's\n * execution quota. Keying makes that leak impossible by construction.\n *\n * Replacement is atomic on the server, so concurrent executions racing to\n * schedule the same key converge on a single task rather than leaking.\n *\n * @param key - Stable identifier for this logical task. Scope it to what it\n * renews, e.g. `` `watch-renewal:${folderId}` ``.\n * @param callback - Callback created with `this.callback()`\n * @param options.runAt - When to run. Required: keying only applies to\n * scheduled tasks (immediate tasks go straight to the queue).\n * @returns Promise resolving to the cancellation token for the scheduled task\n *\n * @example\n * ```typescript\n * const cb = await this.callback(this.renewWatch, folderId);\n * await this.scheduleTask(`watch-renewal:${folderId}`, cb, { runAt });\n * // ...later, on disable:\n * await this.cancelScheduledTask(`watch-renewal:${folderId}`);\n * ```\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract scheduleTask(\n key: string,\n callback: Callback,\n options: { runAt: Date }\n ): Promise<string | void>;\n\n /**\n * Cancels the singleton task previously scheduled under `key` (if any).\n *\n * No error is thrown if no task exists for the key or it has already run.\n * Pair this with {@link scheduleTask} in teardown paths (e.g.\n * `onChannelDisabled`, `stopSync`).\n *\n * @param key - The same key passed to {@link scheduleTask}\n * @returns Promise that resolves when the cancellation is processed\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract cancelScheduledTask(key: string): Promise<void>;\n}\n";
7
+ declare const _default: "import { ITool } from \"..\";\nimport type { Callback } from \"./callbacks\";\n\n/**\n * Run background tasks and scheduled jobs.\n *\n * The Tasks tool enables twists and tools to queue callbacks for execution in separate\n * worker contexts. **This is critical for staying under request limits**: each execution\n * has a limit of ~1000 requests (HTTP requests, tool calls, database operations), and\n * running a task creates a NEW execution with a fresh request limit.\n *\n * **Key distinction:**\n * - **Calling a callback** (via `this.run()`) continues the same execution and shares the request count\n * - **Running a task** (via `this.runTask()`) creates a NEW execution with fresh ~1000 request limit\n *\n * **When to use tasks:**\n * - Processing large datasets that would exceed 1000 requests\n * - Breaking loops into chunks where each chunk stays under the request limit\n * - Scheduling operations for future execution\n *\n * **Note:** Tasks tool methods are also available directly on Twist and Tool classes\n * via `this.runTask()`, `this.cancelTask()`, and `this.cancelAllTasks()`.\n * This is the recommended approach for most use cases.\n *\n * **Best Practices:**\n * - Size batches to stay under ~1000 requests per execution\n * - Calculate requests per item to determine safe batch size\n * - Create callbacks first using `this.callback()`\n * - Store intermediate state using the Store tool\n *\n * @example\n * ```typescript\n * class SyncTool extends Tool<SyncTool> {\n * async startBatchSync(totalItems: number) {\n * // Store initial state using built-in set method\n * await this.set(\"sync_progress\", { processed: 0, total: totalItems });\n *\n * // Create callback and queue first batch\n * const callback = await this.callback(this.processBatch, 1);\n * // runTask creates NEW execution with fresh ~1000 request limit\n * await this.runTask(callback);\n * }\n *\n * async processBatch(batchNumber: number) {\n * // Process one batch of items (sized to stay under request limit)\n * const progress = await this.get(\"sync_progress\");\n *\n * // If each item makes ~10 requests, process ~100 items per batch\n * // 100 items \u00D7 10 requests = 1000 requests (at limit)\n * const batchSize = 100;\n * const items = await this.fetchItems(progress.processed, batchSize);\n *\n * for (const item of items) {\n * await this.processItem(item); // Makes ~10 requests per item\n * }\n *\n * await this.set(\"sync_progress\", {\n * processed: progress.processed + batchSize,\n * total: progress.total\n * });\n *\n * if (progress.processed < progress.total) {\n * // Queue next batch - creates NEW execution with fresh request limit\n * const callback = await this.callback(this.processBatch, batchNumber + 1);\n * await this.runTask(callback);\n * }\n * }\n *\n * async scheduleCleanup() {\n * const tomorrow = new Date();\n * tomorrow.setDate(tomorrow.getDate() + 1);\n *\n * const callback = await this.callback(this.cleanupOldData);\n * // Schedule for future execution\n * return await this.runTask(callback, { runAt: tomorrow });\n * }\n * }\n * ```\n */\nexport abstract class Tasks extends ITool {\n /**\n * Queues a callback to execute in a separate worker context with a fresh request limit.\n *\n * **Creates a NEW execution** with its own request limit of ~1000 requests (HTTP requests,\n * tool calls, database operations). This is the primary way to stay under request limits\n * when processing large datasets or making many API calls.\n *\n * The callback will be invoked either immediately or at a scheduled time\n * in an isolated execution environment. Each execution has ~1000 requests and ~60 seconds\n * CPU time. Use this for breaking loops into chunks that stay under the request limit.\n *\n * **Key distinction:**\n * - `this.run(callback)` - Continues same execution, shares request count\n * - `this.runTask(callback)` - NEW execution, fresh request limit\n *\n * @param callback - Callback created with `this.callback()`\n * @param options - Optional configuration for the execution\n * @param options.runAt - If provided, schedules execution at this time; otherwise runs immediately\n * @returns Promise resolving to a cancellation token (only for scheduled executions)\n *\n * @example\n * ```typescript\n * // Break large loop into batches to stay under request limit\n * const callback = await this.callback(this.syncBatch, 1);\n * await this.runTask(callback); // Fresh execution with ~1000 requests\n * ```\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract runTask(\n callback: Callback,\n options?: { runAt?: Date }\n ): Promise<string | void>;\n\n /**\n * Cancels a previously scheduled execution.\n *\n * Prevents a scheduled function from executing. No error is thrown\n * if the token is invalid or the execution has already completed.\n *\n * @param token - The cancellation token returned by runTask() with runAt option\n * @returns Promise that resolves when the cancellation is processed\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract cancelTask(token: string): Promise<void>;\n\n /**\n * Cancels all scheduled executions for this tool/twist.\n *\n * Cancels all pending scheduled executions created by this tool or twist\n * instance. Immediate executions cannot be cancelled.\n *\n * @returns Promise that resolves when all cancellations are processed\n */\n abstract cancelAllTasks(): Promise<void>;\n\n /**\n * Schedules a **one-shot singleton** task identified by `key`: scheduling\n * under a key that already has a pending task atomically cancels the existing\n * one and replaces it. At most one scheduled task per `key` is ever live.\n *\n * Use this for **one-shot keyed deferred work** \u2014 a single future task whose\n * pending occurrence should be atomically replaced if re-scheduled (e.g.\n * a deferred cleanup, a one-time expiry action, a single future send).\n *\n * For **recurring/self-renewing jobs** (watch renewals, polling loops,\n * periodic syncs, self-heal checks), use {@link scheduleRecurring} instead.\n * It owns the cadence on the platform side, so the chain survives dropped\n * runs, suspensions, and deploys without the callback needing to reschedule\n * itself.\n *\n * Replacement is atomic on the server, so concurrent executions racing to\n * schedule the same key converge on a single task rather than leaking.\n *\n * @param key - Stable identifier for this logical task. Scope it to what it\n * renews, e.g. `` `watch-renewal:${folderId}` ``.\n * @param callback - Callback created with `this.callback()`\n * @param options.runAt - When to run. Required: keying only applies to\n * scheduled tasks (immediate tasks go straight to the queue).\n * @returns Promise resolving to the cancellation token for the scheduled task\n *\n * @example\n * ```typescript\n * const cb = await this.callback(this.renewWatch, folderId);\n * await this.scheduleTask(`watch-renewal:${folderId}`, cb, { runAt });\n * // ...later, on disable:\n * await this.cancelScheduledTask(`watch-renewal:${folderId}`);\n * ```\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract scheduleTask(\n key: string,\n callback: Callback,\n options: { runAt: Date }\n ): Promise<string | void>;\n\n /**\n * Cancels the singleton task previously scheduled under `key` (if any).\n *\n * No error is thrown if no task exists for the key or it has already run.\n * Pair this with {@link scheduleTask} in teardown paths (e.g.\n * `onChannelDisabled`, `stopSync`).\n *\n * @param key - The same key passed to {@link scheduleTask}\n * @returns Promise that resolves when the cancellation is processed\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract cancelScheduledTask(key: string): Promise<void>;\n\n /**\n * Schedules a **durable recurring** task identified by `key`. Unlike\n * {@link scheduleTask} (one-shot, deleted when it fires), a recurring task's\n * next occurrence is owned by the platform: the runtime re-arms it every\n * `intervalMs` automatically, so the chain survives a dropped queue message,\n * a suspension, a deploy/eviction, or a callback that throws before it could\n * reschedule. The callback just does the work, idempotently \u2014 it does NOT\n * need to reschedule itself.\n *\n * `intervalMs` is a **safety ceiling** (the maximum gap between fires). For\n * data-dependent cadence (e.g. renew 24h before a provider-returned expiry),\n * pass `firstRunAt` for the precise next fire and re-call `scheduleRecurring`\n * with the same key on each run to keep tightening it; the ceiling guarantees\n * the chain still fires if a run is lost. `firstRunAt` can pull the next fire\n * earlier than the ceiling but never later.\n *\n * Recurring tasks are keyed/singleton: re-scheduling under the same key\n * atomically replaces the pending occurrence (one live task per key). Tear\n * down with {@link cancelScheduledTask}.\n *\n * @param key - Stable identifier, scoped to what it maintains, e.g.\n * `` `watch-renewal:${folderId}` `` or `\"mailbox-self-heal\"`.\n * @param callback - Callback created with `this.callback()`.\n * @param options.intervalMs - Safety-ceiling cadence in milliseconds.\n * @param options.firstRunAt - Optional precise time for the next fire\n * (clamped to no later than now + intervalMs).\n *\n * @example\n * ```typescript\n * // Fixed cadence (self-heal, polling): register once, never reschedule.\n * const cb = await this.callback(this.selfHealCheck);\n * await this.scheduleRecurring(\"mailbox-self-heal\", cb, { intervalMs: 60 * 60 * 1000 });\n *\n * // Variable cadence (watch renewal): precise firstRunAt + safety ceiling.\n * const renew = await this.callback(this.renewWatch, folderId);\n * await this.scheduleRecurring(`watch-renewal:${folderId}`, renew, {\n * intervalMs: 3.5 * 24 * 60 * 60 * 1000, // ceiling: half the 7-day watch\n * firstRunAt: new Date(expiry.getTime() - 24 * 60 * 60 * 1000),\n * });\n * ```\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract scheduleRecurring(\n key: string,\n callback: Callback,\n options: { intervalMs: number; firstRunAt?: Date }\n ): Promise<void>;\n}\n";
8
8
  export default _default;
9
9
  //# sourceMappingURL=tasks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../../src/llm-docs/tools/tasks.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;wBAEY,6+PAA89P;AAA7+P,wBAA8+P"}
1
+ {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../../src/llm-docs/tools/tasks.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;wBAEY,k4UAAm3U;AAAl4U,wBAAm4U"}
@@ -4,5 +4,5 @@
4
4
  * This file is auto-generated during build. Do not edit manually.
5
5
  * Generated from: prebuild.ts
6
6
  */
7
- export default "import { ITool } from \"..\";\nimport type { Callback } from \"./callbacks\";\n\n/**\n * Run background tasks and scheduled jobs.\n *\n * The Tasks tool enables twists and tools to queue callbacks for execution in separate\n * worker contexts. **This is critical for staying under request limits**: each execution\n * has a limit of ~1000 requests (HTTP requests, tool calls, database operations), and\n * running a task creates a NEW execution with a fresh request limit.\n *\n * **Key distinction:**\n * - **Calling a callback** (via `this.run()`) continues the same execution and shares the request count\n * - **Running a task** (via `this.runTask()`) creates a NEW execution with fresh ~1000 request limit\n *\n * **When to use tasks:**\n * - Processing large datasets that would exceed 1000 requests\n * - Breaking loops into chunks where each chunk stays under the request limit\n * - Scheduling operations for future execution\n *\n * **Note:** Tasks tool methods are also available directly on Twist and Tool classes\n * via `this.runTask()`, `this.cancelTask()`, and `this.cancelAllTasks()`.\n * This is the recommended approach for most use cases.\n *\n * **Best Practices:**\n * - Size batches to stay under ~1000 requests per execution\n * - Calculate requests per item to determine safe batch size\n * - Create callbacks first using `this.callback()`\n * - Store intermediate state using the Store tool\n *\n * @example\n * ```typescript\n * class SyncTool extends Tool<SyncTool> {\n * async startBatchSync(totalItems: number) {\n * // Store initial state using built-in set method\n * await this.set(\"sync_progress\", { processed: 0, total: totalItems });\n *\n * // Create callback and queue first batch\n * const callback = await this.callback(this.processBatch, 1);\n * // runTask creates NEW execution with fresh ~1000 request limit\n * await this.runTask(callback);\n * }\n *\n * async processBatch(batchNumber: number) {\n * // Process one batch of items (sized to stay under request limit)\n * const progress = await this.get(\"sync_progress\");\n *\n * // If each item makes ~10 requests, process ~100 items per batch\n * // 100 items × 10 requests = 1000 requests (at limit)\n * const batchSize = 100;\n * const items = await this.fetchItems(progress.processed, batchSize);\n *\n * for (const item of items) {\n * await this.processItem(item); // Makes ~10 requests per item\n * }\n *\n * await this.set(\"sync_progress\", {\n * processed: progress.processed + batchSize,\n * total: progress.total\n * });\n *\n * if (progress.processed < progress.total) {\n * // Queue next batch - creates NEW execution with fresh request limit\n * const callback = await this.callback(this.processBatch, batchNumber + 1);\n * await this.runTask(callback);\n * }\n * }\n *\n * async scheduleCleanup() {\n * const tomorrow = new Date();\n * tomorrow.setDate(tomorrow.getDate() + 1);\n *\n * const callback = await this.callback(this.cleanupOldData);\n * // Schedule for future execution\n * return await this.runTask(callback, { runAt: tomorrow });\n * }\n * }\n * ```\n */\nexport abstract class Tasks extends ITool {\n /**\n * Queues a callback to execute in a separate worker context with a fresh request limit.\n *\n * **Creates a NEW execution** with its own request limit of ~1000 requests (HTTP requests,\n * tool calls, database operations). This is the primary way to stay under request limits\n * when processing large datasets or making many API calls.\n *\n * The callback will be invoked either immediately or at a scheduled time\n * in an isolated execution environment. Each execution has ~1000 requests and ~60 seconds\n * CPU time. Use this for breaking loops into chunks that stay under the request limit.\n *\n * **Key distinction:**\n * - `this.run(callback)` - Continues same execution, shares request count\n * - `this.runTask(callback)` - NEW execution, fresh request limit\n *\n * @param callback - Callback created with `this.callback()`\n * @param options - Optional configuration for the execution\n * @param options.runAt - If provided, schedules execution at this time; otherwise runs immediately\n * @returns Promise resolving to a cancellation token (only for scheduled executions)\n *\n * @example\n * ```typescript\n * // Break large loop into batches to stay under request limit\n * const callback = await this.callback(this.syncBatch, 1);\n * await this.runTask(callback); // Fresh execution with ~1000 requests\n * ```\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract runTask(\n callback: Callback,\n options?: { runAt?: Date }\n ): Promise<string | void>;\n\n /**\n * Cancels a previously scheduled execution.\n *\n * Prevents a scheduled function from executing. No error is thrown\n * if the token is invalid or the execution has already completed.\n *\n * @param token - The cancellation token returned by runTask() with runAt option\n * @returns Promise that resolves when the cancellation is processed\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract cancelTask(token: string): Promise<void>;\n\n /**\n * Cancels all scheduled executions for this tool/twist.\n *\n * Cancels all pending scheduled executions created by this tool or twist\n * instance. Immediate executions cannot be cancelled.\n *\n * @returns Promise that resolves when all cancellations are processed\n */\n abstract cancelAllTasks(): Promise<void>;\n\n /**\n * Schedules a **singleton** task identified by `key`: scheduling under a key\n * that already has a pending task atomically cancels the existing one and\n * replaces it. At most one scheduled task per `key` is ever live.\n *\n * Use this for any recurring/self-renewing job — webhook/watch renewals,\n * periodic polling, deferred cleanup — instead of hand-managing tokens with\n * `runTask()` + `cancelTask()`. The manual pattern (store the token, cancel\n * it before re-scheduling) is easy to get wrong: a renewal callback that\n * re-schedules itself, combined with any *extra* scheduling call (a\n * re-dispatched `onChannelEnabled`, a re-init), leaks parallel self-\n * perpetuating chains that accumulate forever and can trip the runtime's\n * execution quota. Keying makes that leak impossible by construction.\n *\n * Replacement is atomic on the server, so concurrent executions racing to\n * schedule the same key converge on a single task rather than leaking.\n *\n * @param key - Stable identifier for this logical task. Scope it to what it\n * renews, e.g. `` `watch-renewal:${folderId}` ``.\n * @param callback - Callback created with `this.callback()`\n * @param options.runAt - When to run. Required: keying only applies to\n * scheduled tasks (immediate tasks go straight to the queue).\n * @returns Promise resolving to the cancellation token for the scheduled task\n *\n * @example\n * ```typescript\n * const cb = await this.callback(this.renewWatch, folderId);\n * await this.scheduleTask(`watch-renewal:${folderId}`, cb, { runAt });\n * // ...later, on disable:\n * await this.cancelScheduledTask(`watch-renewal:${folderId}`);\n * ```\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract scheduleTask(\n key: string,\n callback: Callback,\n options: { runAt: Date }\n ): Promise<string | void>;\n\n /**\n * Cancels the singleton task previously scheduled under `key` (if any).\n *\n * No error is thrown if no task exists for the key or it has already run.\n * Pair this with {@link scheduleTask} in teardown paths (e.g.\n * `onChannelDisabled`, `stopSync`).\n *\n * @param key - The same key passed to {@link scheduleTask}\n * @returns Promise that resolves when the cancellation is processed\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract cancelScheduledTask(key: string): Promise<void>;\n}\n";
7
+ export default "import { ITool } from \"..\";\nimport type { Callback } from \"./callbacks\";\n\n/**\n * Run background tasks and scheduled jobs.\n *\n * The Tasks tool enables twists and tools to queue callbacks for execution in separate\n * worker contexts. **This is critical for staying under request limits**: each execution\n * has a limit of ~1000 requests (HTTP requests, tool calls, database operations), and\n * running a task creates a NEW execution with a fresh request limit.\n *\n * **Key distinction:**\n * - **Calling a callback** (via `this.run()`) continues the same execution and shares the request count\n * - **Running a task** (via `this.runTask()`) creates a NEW execution with fresh ~1000 request limit\n *\n * **When to use tasks:**\n * - Processing large datasets that would exceed 1000 requests\n * - Breaking loops into chunks where each chunk stays under the request limit\n * - Scheduling operations for future execution\n *\n * **Note:** Tasks tool methods are also available directly on Twist and Tool classes\n * via `this.runTask()`, `this.cancelTask()`, and `this.cancelAllTasks()`.\n * This is the recommended approach for most use cases.\n *\n * **Best Practices:**\n * - Size batches to stay under ~1000 requests per execution\n * - Calculate requests per item to determine safe batch size\n * - Create callbacks first using `this.callback()`\n * - Store intermediate state using the Store tool\n *\n * @example\n * ```typescript\n * class SyncTool extends Tool<SyncTool> {\n * async startBatchSync(totalItems: number) {\n * // Store initial state using built-in set method\n * await this.set(\"sync_progress\", { processed: 0, total: totalItems });\n *\n * // Create callback and queue first batch\n * const callback = await this.callback(this.processBatch, 1);\n * // runTask creates NEW execution with fresh ~1000 request limit\n * await this.runTask(callback);\n * }\n *\n * async processBatch(batchNumber: number) {\n * // Process one batch of items (sized to stay under request limit)\n * const progress = await this.get(\"sync_progress\");\n *\n * // If each item makes ~10 requests, process ~100 items per batch\n * // 100 items × 10 requests = 1000 requests (at limit)\n * const batchSize = 100;\n * const items = await this.fetchItems(progress.processed, batchSize);\n *\n * for (const item of items) {\n * await this.processItem(item); // Makes ~10 requests per item\n * }\n *\n * await this.set(\"sync_progress\", {\n * processed: progress.processed + batchSize,\n * total: progress.total\n * });\n *\n * if (progress.processed < progress.total) {\n * // Queue next batch - creates NEW execution with fresh request limit\n * const callback = await this.callback(this.processBatch, batchNumber + 1);\n * await this.runTask(callback);\n * }\n * }\n *\n * async scheduleCleanup() {\n * const tomorrow = new Date();\n * tomorrow.setDate(tomorrow.getDate() + 1);\n *\n * const callback = await this.callback(this.cleanupOldData);\n * // Schedule for future execution\n * return await this.runTask(callback, { runAt: tomorrow });\n * }\n * }\n * ```\n */\nexport abstract class Tasks extends ITool {\n /**\n * Queues a callback to execute in a separate worker context with a fresh request limit.\n *\n * **Creates a NEW execution** with its own request limit of ~1000 requests (HTTP requests,\n * tool calls, database operations). This is the primary way to stay under request limits\n * when processing large datasets or making many API calls.\n *\n * The callback will be invoked either immediately or at a scheduled time\n * in an isolated execution environment. Each execution has ~1000 requests and ~60 seconds\n * CPU time. Use this for breaking loops into chunks that stay under the request limit.\n *\n * **Key distinction:**\n * - `this.run(callback)` - Continues same execution, shares request count\n * - `this.runTask(callback)` - NEW execution, fresh request limit\n *\n * @param callback - Callback created with `this.callback()`\n * @param options - Optional configuration for the execution\n * @param options.runAt - If provided, schedules execution at this time; otherwise runs immediately\n * @returns Promise resolving to a cancellation token (only for scheduled executions)\n *\n * @example\n * ```typescript\n * // Break large loop into batches to stay under request limit\n * const callback = await this.callback(this.syncBatch, 1);\n * await this.runTask(callback); // Fresh execution with ~1000 requests\n * ```\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract runTask(\n callback: Callback,\n options?: { runAt?: Date }\n ): Promise<string | void>;\n\n /**\n * Cancels a previously scheduled execution.\n *\n * Prevents a scheduled function from executing. No error is thrown\n * if the token is invalid or the execution has already completed.\n *\n * @param token - The cancellation token returned by runTask() with runAt option\n * @returns Promise that resolves when the cancellation is processed\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract cancelTask(token: string): Promise<void>;\n\n /**\n * Cancels all scheduled executions for this tool/twist.\n *\n * Cancels all pending scheduled executions created by this tool or twist\n * instance. Immediate executions cannot be cancelled.\n *\n * @returns Promise that resolves when all cancellations are processed\n */\n abstract cancelAllTasks(): Promise<void>;\n\n /**\n * Schedules a **one-shot singleton** task identified by `key`: scheduling\n * under a key that already has a pending task atomically cancels the existing\n * one and replaces it. At most one scheduled task per `key` is ever live.\n *\n * Use this for **one-shot keyed deferred work** — a single future task whose\n * pending occurrence should be atomically replaced if re-scheduled (e.g.\n * a deferred cleanup, a one-time expiry action, a single future send).\n *\n * For **recurring/self-renewing jobs** (watch renewals, polling loops,\n * periodic syncs, self-heal checks), use {@link scheduleRecurring} instead.\n * It owns the cadence on the platform side, so the chain survives dropped\n * runs, suspensions, and deploys without the callback needing to reschedule\n * itself.\n *\n * Replacement is atomic on the server, so concurrent executions racing to\n * schedule the same key converge on a single task rather than leaking.\n *\n * @param key - Stable identifier for this logical task. Scope it to what it\n * renews, e.g. `` `watch-renewal:${folderId}` ``.\n * @param callback - Callback created with `this.callback()`\n * @param options.runAt - When to run. Required: keying only applies to\n * scheduled tasks (immediate tasks go straight to the queue).\n * @returns Promise resolving to the cancellation token for the scheduled task\n *\n * @example\n * ```typescript\n * const cb = await this.callback(this.renewWatch, folderId);\n * await this.scheduleTask(`watch-renewal:${folderId}`, cb, { runAt });\n * // ...later, on disable:\n * await this.cancelScheduledTask(`watch-renewal:${folderId}`);\n * ```\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract scheduleTask(\n key: string,\n callback: Callback,\n options: { runAt: Date }\n ): Promise<string | void>;\n\n /**\n * Cancels the singleton task previously scheduled under `key` (if any).\n *\n * No error is thrown if no task exists for the key or it has already run.\n * Pair this with {@link scheduleTask} in teardown paths (e.g.\n * `onChannelDisabled`, `stopSync`).\n *\n * @param key - The same key passed to {@link scheduleTask}\n * @returns Promise that resolves when the cancellation is processed\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract cancelScheduledTask(key: string): Promise<void>;\n\n /**\n * Schedules a **durable recurring** task identified by `key`. Unlike\n * {@link scheduleTask} (one-shot, deleted when it fires), a recurring task's\n * next occurrence is owned by the platform: the runtime re-arms it every\n * `intervalMs` automatically, so the chain survives a dropped queue message,\n * a suspension, a deploy/eviction, or a callback that throws before it could\n * reschedule. The callback just does the work, idempotently — it does NOT\n * need to reschedule itself.\n *\n * `intervalMs` is a **safety ceiling** (the maximum gap between fires). For\n * data-dependent cadence (e.g. renew 24h before a provider-returned expiry),\n * pass `firstRunAt` for the precise next fire and re-call `scheduleRecurring`\n * with the same key on each run to keep tightening it; the ceiling guarantees\n * the chain still fires if a run is lost. `firstRunAt` can pull the next fire\n * earlier than the ceiling but never later.\n *\n * Recurring tasks are keyed/singleton: re-scheduling under the same key\n * atomically replaces the pending occurrence (one live task per key). Tear\n * down with {@link cancelScheduledTask}.\n *\n * @param key - Stable identifier, scoped to what it maintains, e.g.\n * `` `watch-renewal:${folderId}` `` or `\"mailbox-self-heal\"`.\n * @param callback - Callback created with `this.callback()`.\n * @param options.intervalMs - Safety-ceiling cadence in milliseconds.\n * @param options.firstRunAt - Optional precise time for the next fire\n * (clamped to no later than now + intervalMs).\n *\n * @example\n * ```typescript\n * // Fixed cadence (self-heal, polling): register once, never reschedule.\n * const cb = await this.callback(this.selfHealCheck);\n * await this.scheduleRecurring(\"mailbox-self-heal\", cb, { intervalMs: 60 * 60 * 1000 });\n *\n * // Variable cadence (watch renewal): precise firstRunAt + safety ceiling.\n * const renew = await this.callback(this.renewWatch, folderId);\n * await this.scheduleRecurring(`watch-renewal:${folderId}`, renew, {\n * intervalMs: 3.5 * 24 * 60 * 60 * 1000, // ceiling: half the 7-day watch\n * firstRunAt: new Date(expiry.getTime() - 24 * 60 * 60 * 1000),\n * });\n * ```\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n abstract scheduleRecurring(\n key: string,\n callback: Callback,\n options: { intervalMs: number; firstRunAt?: Date }\n ): Promise<void>;\n}\n";
8
8
  //# sourceMappingURL=tasks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../../src/llm-docs/tools/tasks.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAe,89PAA89P,CAAC"}
1
+ {"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../../src/llm-docs/tools/tasks.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAe,m3UAAm3U,CAAC"}
@@ -4,6 +4,6 @@
4
4
  * This file is auto-generated during build. Do not edit manually.
5
5
  * Generated from: prebuild.ts
6
6
  */
7
- declare const _default: "import type { NoteWriteBackResult } from \"./connector\";\nimport { type Action, type Actor, type ActorId, type Link, type Note, type Thread, Uuid } from \"./plot\";\nimport type { Tag } from \"./tag\";\nimport { type ITool } from \"./tool\";\nimport type { Callback } from \"./tools/callbacks\";\nimport type { Serializable } from \"./utils/serializable\";\nimport type { InferTools, ToolBuilder, ToolShed } from \"./utils/types\";\n\n/**\n * Base class for all twists.\n *\n * A twist is installed at the workspace level and is owned by a single user\n * (see `this.userId`). It has no inherent focus scope: threads, notes, and\n * links it creates are filed against the owner's focuses, with automatic\n * focus matching when no explicit target is provided.\n *\n * Override `build()` to declare tool dependencies and lifecycle methods to\n * handle events.\n *\n * @example\n * ```typescript\n * class FlatteringTwist extends Twist<FlatteringTwist> {\n * build(build: ToolBuilder) {\n * return {\n * plot: build(Plot),\n * };\n * }\n *\n * async activate() {\n * await this.tools.plot.createThread({\n * title: \"Hello, good looking!\",\n * });\n * }\n * }\n * ```\n */\nexport abstract class Twist<TSelf> {\n /**\n * When `true`, users may install multiple instances of this twist within\n * the same scope (personal workspace or team). Each instance must have a\n * distinct name.\n *\n * Defaults to `false` (single instance per scope).\n *\n * @example\n * ```typescript\n * class WorkflowTwist extends Twist<WorkflowTwist> {\n * static readonly multipleInstances = true;\n * // ...\n * }\n * ```\n */\n static readonly multipleInstances?: boolean;\n\n /**\n * The user ID (`twist_instance.owner_id`) that installed this twist.\n * Populated by the runtime before any lifecycle method runs.\n */\n protected userId!: Uuid;\n\n constructor(protected id: Uuid, private toolShed: ToolShed) {}\n\n /**\n * Gets the initialized tools for this twist.\n * @throws Error if called before initialization is complete\n */\n protected get tools(): InferTools<TSelf> {\n return this.toolShed.getTools<InferTools<TSelf>>();\n }\n\n /**\n * Declares tool dependencies for this twist.\n * Return an object mapping tool names to build() promises.\n *\n * @param build - The build function to use for declaring dependencies\n * @returns Object mapping tool names to tool promises\n *\n * @example\n * ```typescript\n * build(build: ToolBuilder) {\n * return {\n * plot: build(Plot),\n * calendar: build(GoogleCalendar, { apiKey: \"...\" }),\n * };\n * }\n * ```\n */\n abstract build(build: ToolBuilder): Record<string, Promise<ITool>>;\n\n /**\n * Creates a persistent callback to a method on this twist.\n *\n * ExtraArgs are strongly typed to match the method's signature. They must be serializable.\n *\n * @param fn - The method to callback\n * @param extraArgs - Additional arguments to pass (type-checked, must be serializable)\n * @returns Promise resolving to a persistent callback token\n *\n * @example\n * ```typescript\n * const callback = await this.callback(this.onWebhook, \"calendar\", 123);\n * ```\n */\n protected callback<\n TArgs extends Serializable[],\n Fn extends (...args: TArgs) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback>;\n // Overload when caller provides the first argument\n protected callback<\n TArgs extends Serializable[],\n Fn extends (arg1: any, ...extraArgs: TArgs) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback>;\n protected async callback<\n TArgs extends Serializable[],\n Fn extends (...args: any[]) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback> {\n return this.tools.callbacks.create(fn, ...extraArgs);\n }\n\n /**\n * Like callback(), but for an Action, which receives the action as the first argument.\n *\n * @param fn - The method to callback\n * @param extraArgs - Additional arguments to pass after the action\n * @returns Promise resolving to a persistent callback token\n *\n * @example\n * ```typescript\n * const callback = await this.actionCallback(this.doSomething, 123);\n * const action: Action = {\n * type: ActionType.callback,\n * title: \"Do Something\",\n * callback,\n * };\n * ```\n */\n protected async actionCallback<\n TArgs extends Serializable[],\n Fn extends (action: Action, ...extraArgs: TArgs) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback> {\n return this.tools.callbacks.create(fn, ...extraArgs);\n }\n\n /**\n * Deletes a specific callback by its token.\n *\n * @param token - The callback token to delete\n * @returns Promise that resolves when the callback is deleted\n */\n protected async deleteCallback(token: Callback): Promise<void> {\n return this.tools.callbacks.delete(token);\n }\n\n /**\n * Deletes all callbacks for this twist.\n *\n * @returns Promise that resolves when all callbacks are deleted\n */\n protected async deleteAllCallbacks(): Promise<void> {\n return this.tools.callbacks.deleteAll();\n }\n\n /**\n * Executes a callback by its token inline in the current execution.\n *\n * **Use `this.runTask()` instead for batch continuations and long-running work.**\n * `this.run()` executes inline, sharing the current request count (~1000 limit)\n * and blocking the HTTP response. This causes timeouts when used in lifecycle\n * methods like `onChannelEnabled` or `syncBatch` continuations.\n *\n * `this.run()` is appropriate when you need the callback's **return value** \u2014\n * e.g., running a parent callback token that returns data. For fire-and-forget\n * work, always prefer `this.runTask()`.\n *\n * @param token - The callback token to execute\n * @param args - Optional arguments to pass to the callback\n * @returns Promise resolving to the callback result\n */\n protected async run(token: Callback, ...args: []): Promise<any> {\n return this.tools.callbacks.run(token, ...args);\n }\n\n /**\n * Retrieves a value from persistent storage by key.\n *\n * Values are automatically deserialized using SuperJSON, which\n * properly restores Date objects, Maps, Sets, and other complex types.\n *\n * @template T - The expected type of the stored value (must be Serializable)\n * @param key - The storage key to retrieve\n * @returns Promise resolving to the stored value or null\n */\n protected async get<T extends import(\"./index\").Serializable>(\n key: string\n ): Promise<T | null> {\n return this.tools.store.get(key);\n }\n\n /**\n * Stores a value in persistent storage.\n *\n * The value will be serialized using SuperJSON and stored persistently.\n * SuperJSON automatically handles Date objects, Maps, Sets, undefined values,\n * and other complex types that standard JSON doesn't support.\n *\n * **Important**: Functions and Symbols cannot be stored.\n * **For function references**: Use callbacks instead of storing functions directly.\n *\n * @example\n * ```typescript\n * // \u2705 Date objects are preserved\n * await this.set(\"sync_state\", {\n * lastSync: new Date(),\n * minDate: new Date(2024, 0, 1)\n * });\n *\n * // \u2705 undefined is now supported\n * await this.set(\"data\", { name: \"test\", optional: undefined });\n *\n * // \u274C WRONG: Cannot store functions directly\n * await this.set(\"handler\", this.myHandler);\n *\n * // \u2705 CORRECT: Create a callback token first\n * const token = await this.callback(this.myHandler, \"arg1\", \"arg2\");\n * await this.set(\"handler_token\", token);\n *\n * // Later, execute the callback\n * const token = await this.get<Callback>(\"handler_token\");\n * await this.run(token);\n * ```\n *\n * @template T - The type of value being stored (must be Serializable)\n * @param key - The storage key to use\n * @param value - The value to store (must be SuperJSON-serializable)\n * @returns Promise that resolves when the value is stored\n */\n protected async set<T extends import(\"./index\").Serializable>(\n key: string,\n value: T\n ): Promise<void> {\n return this.tools.store.set(key, value);\n }\n\n /**\n * Removes a specific key from persistent storage.\n *\n * @param key - The storage key to remove\n * @returns Promise that resolves when the key is removed\n */\n protected async clear(key: string): Promise<void> {\n return this.tools.store.clear(key);\n }\n\n /**\n * Removes all keys from this twist's storage.\n *\n * @returns Promise that resolves when all keys are removed\n */\n protected async clearAll(): Promise<void> {\n return this.tools.store.clearAll();\n }\n\n /**\n * Queues a callback to execute in a separate worker context.\n *\n * @param callback - The callback token created with `this.callback()`\n * @param options - Optional configuration for the execution\n * @param options.runAt - If provided, schedules execution at this time; otherwise runs immediately\n * @returns Promise resolving to a cancellation token (only for scheduled executions)\n */\n protected async runTask(\n callback: Callback,\n options?: { runAt?: Date }\n ): Promise<string | void> {\n return this.tools.tasks.runTask(callback, options);\n }\n\n /**\n * Cancels a previously scheduled execution.\n *\n * @param token - The cancellation token returned by runTask() with runAt option\n * @returns Promise that resolves when the cancellation is processed\n */\n protected async cancelTask(token: string): Promise<void> {\n return this.tools.tasks.cancelTask(token);\n }\n\n /**\n * Cancels all scheduled executions for this twist.\n *\n * @returns Promise that resolves when all cancellations are processed\n */\n protected async cancelAllTasks(): Promise<void> {\n return this.tools.tasks.cancelAllTasks();\n }\n\n /**\n * Schedules a **singleton** task keyed by `key`: re-scheduling under the same\n * key atomically replaces any pending task, so at most one is ever live.\n *\n * Prefer this over `runTask({ runAt })` for recurring/self-renewing jobs\n * (watch renewals, polling, deferred cleanup) \u2014 it removes the error-prone\n * \"store token, cancel before re-scheduling\" bookkeeping that otherwise leaks\n * parallel task chains. See {@link Tasks.scheduleTask}.\n *\n * @param key - Stable identifier scoped to what the task renews\n * @param callback - The callback token created with `this.callback()`\n * @param options.runAt - When to run (required)\n * @returns Promise resolving to the scheduled task's cancellation token\n */\n protected async scheduleTask(\n key: string,\n callback: Callback,\n options: { runAt: Date }\n ): Promise<string | void> {\n return this.tools.tasks.scheduleTask(key, callback, options);\n }\n\n /**\n * Cancels the singleton task previously scheduled under `key` (if any).\n * No-op if none exists or it already ran. See {@link Tasks.cancelScheduledTask}.\n *\n * @param key - The same key passed to {@link scheduleTask}\n * @returns Promise that resolves when the cancellation is processed\n */\n protected async cancelScheduledTask(key: string): Promise<void> {\n return this.tools.tasks.cancelScheduledTask(key);\n }\n\n /**\n * Called when the twist is installed by a user.\n *\n * This method should contain initialization logic such as seeding\n * initial threads, configuring webhooks, or establishing external\n * connections. When it runs, `this.userId` is already populated with\n * the installing user's ID.\n *\n * @param context - Optional context containing the actor who triggered activation\n * @returns Promise that resolves when activation is complete\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n activate(context?: { actor: Actor }): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a new version of the twist is deployed.\n *\n * This method should contain migration logic for updating old data structures\n * or setting up new resources that weren't needed by the previous version.\n * It is called once per active twist_instance with the new version.\n *\n * @returns Promise that resolves when upgrade is complete\n */\n upgrade(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when the twist's options configuration changes.\n *\n * Override to react to option changes, e.g. archiving items when a sync\n * type is toggled off, or starting sync when a type is toggled on.\n *\n * @param oldOptions - The previously resolved options\n * @param newOptions - The newly resolved options\n * @returns Promise that resolves when the change is handled\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onOptionsChanged(\n oldOptions: Record<string, any>,\n newOptions: Record<string, any>\n ): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when the twist is uninstalled.\n *\n * This method should contain cleanup logic such as removing webhooks,\n * cleaning up external resources, or performing final data operations.\n *\n * @returns Promise that resolves when deactivation is complete\n */\n deactivate(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a thread created by this twist is updated.\n * Override to implement two-way sync with an external system.\n *\n * @param thread - The updated thread\n * @param changes - Tag additions and removals on the thread\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onThreadUpdated(\n thread: Thread,\n changes: {\n tagsAdded: Record<Tag, ActorId[]>;\n tagsRemoved: Record<Tag, ActorId[]>;\n }\n ): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a note is created on a thread created by this twist.\n * Override to implement two-way sync (e.g. syncing notes as comments).\n *\n * Notes created by the twist itself are filtered out to prevent loops.\n *\n * Returning a string sets the note's `key` for future upsert matching,\n * linking the Plot note to its external counterpart so that subsequent\n * syncs (reactions, edits) update the existing note instead of creating duplicates.\n *\n * @param note - The newly created note\n * @returns Optional note key for external deduplication\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onNoteCreated(note: Note, ...args: any[]): Promise<string | NoteWriteBackResult | void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a link is created in a connected source channel.\n * Requires `link: true` in Plot options.\n *\n * @param link - The newly created link\n * @param notes - Notes on the link's thread\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onLinkCreated(link: Link, notes: Note[]): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a link in a connected source channel is updated.\n * Requires `link: true` in Plot options.\n *\n * @param link - The updated link\n * @param notes - Notes on the link's thread (optional)\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onLinkUpdated(link: Link, notes?: Note[]): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a note is created on a thread with a link from a connected channel.\n * Requires `link: true` in Plot options.\n *\n * @param note - The newly created note\n * @param link - The link associated with the thread\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onLinkNoteCreated(note: Note, link: Link): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Waits for tool initialization to complete.\n * Called automatically by the entrypoint before lifecycle methods.\n * @internal\n */\n async waitForReady(): Promise<void> {\n await this.toolShed.waitForReady();\n }\n}\n";
7
+ declare const _default: "import type { NoteWriteBackResult } from \"./connector\";\nimport { type Action, type Actor, type ActorId, type Link, type Note, type Thread, Uuid } from \"./plot\";\nimport type { Tag } from \"./tag\";\nimport { type ITool } from \"./tool\";\nimport type { Callback } from \"./tools/callbacks\";\nimport type { Serializable } from \"./utils/serializable\";\nimport type { InferTools, ToolBuilder, ToolShed } from \"./utils/types\";\n\n/**\n * Base class for all twists.\n *\n * A twist is installed at the workspace level and is owned by a single user\n * (see `this.userId`). It has no inherent focus scope: threads, notes, and\n * links it creates are filed against the owner's focuses, with automatic\n * focus matching when no explicit target is provided.\n *\n * Override `build()` to declare tool dependencies and lifecycle methods to\n * handle events.\n *\n * @example\n * ```typescript\n * class FlatteringTwist extends Twist<FlatteringTwist> {\n * build(build: ToolBuilder) {\n * return {\n * plot: build(Plot),\n * };\n * }\n *\n * async activate() {\n * await this.tools.plot.createThread({\n * title: \"Hello, good looking!\",\n * });\n * }\n * }\n * ```\n */\nexport abstract class Twist<TSelf> {\n /**\n * When `true`, users may install multiple instances of this twist within\n * the same scope (personal workspace or team). Each instance must have a\n * distinct name.\n *\n * Defaults to `false` (single instance per scope).\n *\n * @example\n * ```typescript\n * class WorkflowTwist extends Twist<WorkflowTwist> {\n * static readonly multipleInstances = true;\n * // ...\n * }\n * ```\n */\n static readonly multipleInstances?: boolean;\n\n /**\n * The user ID (`twist_instance.owner_id`) that installed this twist.\n * Populated by the runtime before any lifecycle method runs.\n */\n protected userId!: Uuid;\n\n constructor(protected id: Uuid, private toolShed: ToolShed) {}\n\n /**\n * Gets the initialized tools for this twist.\n * @throws Error if called before initialization is complete\n */\n protected get tools(): InferTools<TSelf> {\n return this.toolShed.getTools<InferTools<TSelf>>();\n }\n\n /**\n * Declares tool dependencies for this twist.\n * Return an object mapping tool names to build() promises.\n *\n * @param build - The build function to use for declaring dependencies\n * @returns Object mapping tool names to tool promises\n *\n * @example\n * ```typescript\n * build(build: ToolBuilder) {\n * return {\n * plot: build(Plot),\n * calendar: build(GoogleCalendar, { apiKey: \"...\" }),\n * };\n * }\n * ```\n */\n abstract build(build: ToolBuilder): Record<string, Promise<ITool>>;\n\n /**\n * Creates a persistent callback to a method on this twist.\n *\n * ExtraArgs are strongly typed to match the method's signature. They must be serializable.\n *\n * @param fn - The method to callback\n * @param extraArgs - Additional arguments to pass (type-checked, must be serializable)\n * @returns Promise resolving to a persistent callback token\n *\n * @example\n * ```typescript\n * const callback = await this.callback(this.onWebhook, \"calendar\", 123);\n * ```\n */\n protected callback<\n TArgs extends Serializable[],\n Fn extends (...args: TArgs) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback>;\n // Overload when caller provides the first argument\n protected callback<\n TArgs extends Serializable[],\n Fn extends (arg1: any, ...extraArgs: TArgs) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback>;\n protected async callback<\n TArgs extends Serializable[],\n Fn extends (...args: any[]) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback> {\n return this.tools.callbacks.create(fn, ...extraArgs);\n }\n\n /**\n * Like callback(), but for an Action, which receives the action as the first argument.\n *\n * @param fn - The method to callback\n * @param extraArgs - Additional arguments to pass after the action\n * @returns Promise resolving to a persistent callback token\n *\n * @example\n * ```typescript\n * const callback = await this.actionCallback(this.doSomething, 123);\n * const action: Action = {\n * type: ActionType.callback,\n * title: \"Do Something\",\n * callback,\n * };\n * ```\n */\n protected async actionCallback<\n TArgs extends Serializable[],\n Fn extends (action: Action, ...extraArgs: TArgs) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback> {\n return this.tools.callbacks.create(fn, ...extraArgs);\n }\n\n /**\n * Deletes a specific callback by its token.\n *\n * @param token - The callback token to delete\n * @returns Promise that resolves when the callback is deleted\n */\n protected async deleteCallback(token: Callback): Promise<void> {\n return this.tools.callbacks.delete(token);\n }\n\n /**\n * Deletes all callbacks for this twist.\n *\n * @returns Promise that resolves when all callbacks are deleted\n */\n protected async deleteAllCallbacks(): Promise<void> {\n return this.tools.callbacks.deleteAll();\n }\n\n /**\n * Executes a callback by its token inline in the current execution.\n *\n * **Use `this.runTask()` instead for batch continuations and long-running work.**\n * `this.run()` executes inline, sharing the current request count (~1000 limit)\n * and blocking the HTTP response. This causes timeouts when used in lifecycle\n * methods like `onChannelEnabled` or `syncBatch` continuations.\n *\n * `this.run()` is appropriate when you need the callback's **return value** \u2014\n * e.g., running a parent callback token that returns data. For fire-and-forget\n * work, always prefer `this.runTask()`.\n *\n * @param token - The callback token to execute\n * @param args - Optional arguments to pass to the callback\n * @returns Promise resolving to the callback result\n */\n protected async run(token: Callback, ...args: []): Promise<any> {\n return this.tools.callbacks.run(token, ...args);\n }\n\n /**\n * Retrieves a value from persistent storage by key.\n *\n * Values are automatically deserialized using SuperJSON, which\n * properly restores Date objects, Maps, Sets, and other complex types.\n *\n * @template T - The expected type of the stored value (must be Serializable)\n * @param key - The storage key to retrieve\n * @returns Promise resolving to the stored value or null\n */\n protected async get<T extends import(\"./index\").Serializable>(\n key: string\n ): Promise<T | null> {\n return this.tools.store.get(key);\n }\n\n /**\n * Stores a value in persistent storage.\n *\n * The value will be serialized using SuperJSON and stored persistently.\n * SuperJSON automatically handles Date objects, Maps, Sets, undefined values,\n * and other complex types that standard JSON doesn't support.\n *\n * **Important**: Functions and Symbols cannot be stored.\n * **For function references**: Use callbacks instead of storing functions directly.\n *\n * @example\n * ```typescript\n * // \u2705 Date objects are preserved\n * await this.set(\"sync_state\", {\n * lastSync: new Date(),\n * minDate: new Date(2024, 0, 1)\n * });\n *\n * // \u2705 undefined is now supported\n * await this.set(\"data\", { name: \"test\", optional: undefined });\n *\n * // \u274C WRONG: Cannot store functions directly\n * await this.set(\"handler\", this.myHandler);\n *\n * // \u2705 CORRECT: Create a callback token first\n * const token = await this.callback(this.myHandler, \"arg1\", \"arg2\");\n * await this.set(\"handler_token\", token);\n *\n * // Later, execute the callback\n * const token = await this.get<Callback>(\"handler_token\");\n * await this.run(token);\n * ```\n *\n * @template T - The type of value being stored (must be Serializable)\n * @param key - The storage key to use\n * @param value - The value to store (must be SuperJSON-serializable)\n * @returns Promise that resolves when the value is stored\n */\n protected async set<T extends import(\"./index\").Serializable>(\n key: string,\n value: T\n ): Promise<void> {\n return this.tools.store.set(key, value);\n }\n\n /**\n * Removes a specific key from persistent storage.\n *\n * @param key - The storage key to remove\n * @returns Promise that resolves when the key is removed\n */\n protected async clear(key: string): Promise<void> {\n return this.tools.store.clear(key);\n }\n\n /**\n * Removes all keys from this twist's storage.\n *\n * @returns Promise that resolves when all keys are removed\n */\n protected async clearAll(): Promise<void> {\n return this.tools.store.clearAll();\n }\n\n /**\n * Queues a callback to execute in a separate worker context.\n *\n * @param callback - The callback token created with `this.callback()`\n * @param options - Optional configuration for the execution\n * @param options.runAt - If provided, schedules execution at this time; otherwise runs immediately\n * @returns Promise resolving to a cancellation token (only for scheduled executions)\n */\n protected async runTask(\n callback: Callback,\n options?: { runAt?: Date }\n ): Promise<string | void> {\n return this.tools.tasks.runTask(callback, options);\n }\n\n /**\n * Cancels a previously scheduled execution.\n *\n * @param token - The cancellation token returned by runTask() with runAt option\n * @returns Promise that resolves when the cancellation is processed\n */\n protected async cancelTask(token: string): Promise<void> {\n return this.tools.tasks.cancelTask(token);\n }\n\n /**\n * Cancels all scheduled executions for this twist.\n *\n * @returns Promise that resolves when all cancellations are processed\n */\n protected async cancelAllTasks(): Promise<void> {\n return this.tools.tasks.cancelAllTasks();\n }\n\n /**\n * Schedules a **singleton** task keyed by `key`: re-scheduling under the same\n * key atomically replaces any pending task, so at most one is ever live.\n *\n * Prefer this over `runTask({ runAt })` for recurring/self-renewing jobs\n * (watch renewals, polling, deferred cleanup) \u2014 it removes the error-prone\n * \"store token, cancel before re-scheduling\" bookkeeping that otherwise leaks\n * parallel task chains. See {@link Tasks.scheduleTask}.\n *\n * @param key - Stable identifier scoped to what the task renews\n * @param callback - The callback token created with `this.callback()`\n * @param options.runAt - When to run (required)\n * @returns Promise resolving to the scheduled task's cancellation token\n */\n protected async scheduleTask(\n key: string,\n callback: Callback,\n options: { runAt: Date }\n ): Promise<string | void> {\n return this.tools.tasks.scheduleTask(key, callback, options);\n }\n\n /**\n * Cancels the singleton task previously scheduled under `key` (if any).\n * No-op if none exists or it already ran. See {@link Tasks.cancelScheduledTask}.\n *\n * @param key - The same key passed to {@link scheduleTask}\n * @returns Promise that resolves when the cancellation is processed\n */\n protected async cancelScheduledTask(key: string): Promise<void> {\n return this.tools.tasks.cancelScheduledTask(key);\n }\n\n /**\n * Schedules a durable recurring task under a stable key. The platform\n * re-arms the task every `intervalMs` automatically \u2014 the callback does NOT\n * need to reschedule itself. Re-scheduling under the same key atomically\n * replaces the pending occurrence (at most one live task per key). Tear down\n * with {@link cancelScheduledTask}. See {@link Tasks.scheduleRecurring}.\n *\n * @param key - Stable identifier, e.g. `\"mailbox-self-heal\"`\n * @param callback - Callback token created with `this.callback()`\n * @param options.intervalMs - Safety-ceiling cadence in milliseconds\n * @param options.firstRunAt - Optional precise time for the next fire\n */\n protected async scheduleRecurring(\n key: string,\n callback: Callback,\n options: { intervalMs: number; firstRunAt?: Date }\n ): Promise<void> {\n return this.tools.tasks.scheduleRecurring(key, callback, options);\n }\n\n /**\n * Called when the twist is installed by a user.\n *\n * This method should contain initialization logic such as seeding\n * initial threads, configuring webhooks, or establishing external\n * connections. When it runs, `this.userId` is already populated with\n * the installing user's ID.\n *\n * @param context - Optional context containing the actor who triggered activation\n * @returns Promise that resolves when activation is complete\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n activate(context?: { actor: Actor }): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a new version of the twist is deployed.\n *\n * This method should contain migration logic for updating old data structures\n * or setting up new resources that weren't needed by the previous version.\n * It is called once per active twist_instance with the new version.\n *\n * @returns Promise that resolves when upgrade is complete\n */\n upgrade(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when the twist's options configuration changes.\n *\n * Override to react to option changes, e.g. archiving items when a sync\n * type is toggled off, or starting sync when a type is toggled on.\n *\n * @param oldOptions - The previously resolved options\n * @param newOptions - The newly resolved options\n * @returns Promise that resolves when the change is handled\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onOptionsChanged(\n oldOptions: Record<string, any>,\n newOptions: Record<string, any>\n ): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when the twist is uninstalled.\n *\n * This method should contain cleanup logic such as removing webhooks,\n * cleaning up external resources, or performing final data operations.\n *\n * @returns Promise that resolves when deactivation is complete\n */\n deactivate(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a thread created by this twist is updated.\n * Override to implement two-way sync with an external system.\n *\n * @param thread - The updated thread\n * @param changes - Tag additions and removals on the thread\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onThreadUpdated(\n thread: Thread,\n changes: {\n tagsAdded: Record<Tag, ActorId[]>;\n tagsRemoved: Record<Tag, ActorId[]>;\n }\n ): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a note is created on a thread created by this twist.\n * Override to implement two-way sync (e.g. syncing notes as comments).\n *\n * Notes created by the twist itself are filtered out to prevent loops.\n *\n * Returning a string sets the note's `key` for future upsert matching,\n * linking the Plot note to its external counterpart so that subsequent\n * syncs (reactions, edits) update the existing note instead of creating duplicates.\n *\n * @param note - The newly created note\n * @returns Optional note key for external deduplication\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onNoteCreated(note: Note, ...args: any[]): Promise<string | NoteWriteBackResult | void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a link is created in a connected source channel.\n * Requires `link: true` in Plot options.\n *\n * @param link - The newly created link\n * @param notes - Notes on the link's thread\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onLinkCreated(link: Link, notes: Note[]): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a link in a connected source channel is updated.\n * Requires `link: true` in Plot options.\n *\n * @param link - The updated link\n * @param notes - Notes on the link's thread (optional)\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onLinkUpdated(link: Link, notes?: Note[]): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a note is created on a thread with a link from a connected channel.\n * Requires `link: true` in Plot options.\n *\n * @param note - The newly created note\n * @param link - The link associated with the thread\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onLinkNoteCreated(note: Note, link: Link): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Waits for tool initialization to complete.\n * Called automatically by the entrypoint before lifecycle methods.\n * @internal\n */\n async waitForReady(): Promise<void> {\n await this.toolShed.waitForReady();\n }\n}\n";
8
8
  export default _default;
9
9
  //# sourceMappingURL=twist.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"twist.d.ts","sourceRoot":"","sources":["../../src/llm-docs/twist.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;wBAEY,y7fAA25f;AAA16f,wBAA26f"}
1
+ {"version":3,"file":"twist.d.ts","sourceRoot":"","sources":["../../src/llm-docs/twist.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;wBAEY,i2hBAA8zhB;AAA70hB,wBAA80hB"}
@@ -4,5 +4,5 @@
4
4
  * This file is auto-generated during build. Do not edit manually.
5
5
  * Generated from: prebuild.ts
6
6
  */
7
- export default "import type { NoteWriteBackResult } from \"./connector\";\nimport { type Action, type Actor, type ActorId, type Link, type Note, type Thread, Uuid } from \"./plot\";\nimport type { Tag } from \"./tag\";\nimport { type ITool } from \"./tool\";\nimport type { Callback } from \"./tools/callbacks\";\nimport type { Serializable } from \"./utils/serializable\";\nimport type { InferTools, ToolBuilder, ToolShed } from \"./utils/types\";\n\n/**\n * Base class for all twists.\n *\n * A twist is installed at the workspace level and is owned by a single user\n * (see `this.userId`). It has no inherent focus scope: threads, notes, and\n * links it creates are filed against the owner's focuses, with automatic\n * focus matching when no explicit target is provided.\n *\n * Override `build()` to declare tool dependencies and lifecycle methods to\n * handle events.\n *\n * @example\n * ```typescript\n * class FlatteringTwist extends Twist<FlatteringTwist> {\n * build(build: ToolBuilder) {\n * return {\n * plot: build(Plot),\n * };\n * }\n *\n * async activate() {\n * await this.tools.plot.createThread({\n * title: \"Hello, good looking!\",\n * });\n * }\n * }\n * ```\n */\nexport abstract class Twist<TSelf> {\n /**\n * When `true`, users may install multiple instances of this twist within\n * the same scope (personal workspace or team). Each instance must have a\n * distinct name.\n *\n * Defaults to `false` (single instance per scope).\n *\n * @example\n * ```typescript\n * class WorkflowTwist extends Twist<WorkflowTwist> {\n * static readonly multipleInstances = true;\n * // ...\n * }\n * ```\n */\n static readonly multipleInstances?: boolean;\n\n /**\n * The user ID (`twist_instance.owner_id`) that installed this twist.\n * Populated by the runtime before any lifecycle method runs.\n */\n protected userId!: Uuid;\n\n constructor(protected id: Uuid, private toolShed: ToolShed) {}\n\n /**\n * Gets the initialized tools for this twist.\n * @throws Error if called before initialization is complete\n */\n protected get tools(): InferTools<TSelf> {\n return this.toolShed.getTools<InferTools<TSelf>>();\n }\n\n /**\n * Declares tool dependencies for this twist.\n * Return an object mapping tool names to build() promises.\n *\n * @param build - The build function to use for declaring dependencies\n * @returns Object mapping tool names to tool promises\n *\n * @example\n * ```typescript\n * build(build: ToolBuilder) {\n * return {\n * plot: build(Plot),\n * calendar: build(GoogleCalendar, { apiKey: \"...\" }),\n * };\n * }\n * ```\n */\n abstract build(build: ToolBuilder): Record<string, Promise<ITool>>;\n\n /**\n * Creates a persistent callback to a method on this twist.\n *\n * ExtraArgs are strongly typed to match the method's signature. They must be serializable.\n *\n * @param fn - The method to callback\n * @param extraArgs - Additional arguments to pass (type-checked, must be serializable)\n * @returns Promise resolving to a persistent callback token\n *\n * @example\n * ```typescript\n * const callback = await this.callback(this.onWebhook, \"calendar\", 123);\n * ```\n */\n protected callback<\n TArgs extends Serializable[],\n Fn extends (...args: TArgs) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback>;\n // Overload when caller provides the first argument\n protected callback<\n TArgs extends Serializable[],\n Fn extends (arg1: any, ...extraArgs: TArgs) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback>;\n protected async callback<\n TArgs extends Serializable[],\n Fn extends (...args: any[]) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback> {\n return this.tools.callbacks.create(fn, ...extraArgs);\n }\n\n /**\n * Like callback(), but for an Action, which receives the action as the first argument.\n *\n * @param fn - The method to callback\n * @param extraArgs - Additional arguments to pass after the action\n * @returns Promise resolving to a persistent callback token\n *\n * @example\n * ```typescript\n * const callback = await this.actionCallback(this.doSomething, 123);\n * const action: Action = {\n * type: ActionType.callback,\n * title: \"Do Something\",\n * callback,\n * };\n * ```\n */\n protected async actionCallback<\n TArgs extends Serializable[],\n Fn extends (action: Action, ...extraArgs: TArgs) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback> {\n return this.tools.callbacks.create(fn, ...extraArgs);\n }\n\n /**\n * Deletes a specific callback by its token.\n *\n * @param token - The callback token to delete\n * @returns Promise that resolves when the callback is deleted\n */\n protected async deleteCallback(token: Callback): Promise<void> {\n return this.tools.callbacks.delete(token);\n }\n\n /**\n * Deletes all callbacks for this twist.\n *\n * @returns Promise that resolves when all callbacks are deleted\n */\n protected async deleteAllCallbacks(): Promise<void> {\n return this.tools.callbacks.deleteAll();\n }\n\n /**\n * Executes a callback by its token inline in the current execution.\n *\n * **Use `this.runTask()` instead for batch continuations and long-running work.**\n * `this.run()` executes inline, sharing the current request count (~1000 limit)\n * and blocking the HTTP response. This causes timeouts when used in lifecycle\n * methods like `onChannelEnabled` or `syncBatch` continuations.\n *\n * `this.run()` is appropriate when you need the callback's **return value** —\n * e.g., running a parent callback token that returns data. For fire-and-forget\n * work, always prefer `this.runTask()`.\n *\n * @param token - The callback token to execute\n * @param args - Optional arguments to pass to the callback\n * @returns Promise resolving to the callback result\n */\n protected async run(token: Callback, ...args: []): Promise<any> {\n return this.tools.callbacks.run(token, ...args);\n }\n\n /**\n * Retrieves a value from persistent storage by key.\n *\n * Values are automatically deserialized using SuperJSON, which\n * properly restores Date objects, Maps, Sets, and other complex types.\n *\n * @template T - The expected type of the stored value (must be Serializable)\n * @param key - The storage key to retrieve\n * @returns Promise resolving to the stored value or null\n */\n protected async get<T extends import(\"./index\").Serializable>(\n key: string\n ): Promise<T | null> {\n return this.tools.store.get(key);\n }\n\n /**\n * Stores a value in persistent storage.\n *\n * The value will be serialized using SuperJSON and stored persistently.\n * SuperJSON automatically handles Date objects, Maps, Sets, undefined values,\n * and other complex types that standard JSON doesn't support.\n *\n * **Important**: Functions and Symbols cannot be stored.\n * **For function references**: Use callbacks instead of storing functions directly.\n *\n * @example\n * ```typescript\n * // ✅ Date objects are preserved\n * await this.set(\"sync_state\", {\n * lastSync: new Date(),\n * minDate: new Date(2024, 0, 1)\n * });\n *\n * // ✅ undefined is now supported\n * await this.set(\"data\", { name: \"test\", optional: undefined });\n *\n * // ❌ WRONG: Cannot store functions directly\n * await this.set(\"handler\", this.myHandler);\n *\n * // ✅ CORRECT: Create a callback token first\n * const token = await this.callback(this.myHandler, \"arg1\", \"arg2\");\n * await this.set(\"handler_token\", token);\n *\n * // Later, execute the callback\n * const token = await this.get<Callback>(\"handler_token\");\n * await this.run(token);\n * ```\n *\n * @template T - The type of value being stored (must be Serializable)\n * @param key - The storage key to use\n * @param value - The value to store (must be SuperJSON-serializable)\n * @returns Promise that resolves when the value is stored\n */\n protected async set<T extends import(\"./index\").Serializable>(\n key: string,\n value: T\n ): Promise<void> {\n return this.tools.store.set(key, value);\n }\n\n /**\n * Removes a specific key from persistent storage.\n *\n * @param key - The storage key to remove\n * @returns Promise that resolves when the key is removed\n */\n protected async clear(key: string): Promise<void> {\n return this.tools.store.clear(key);\n }\n\n /**\n * Removes all keys from this twist's storage.\n *\n * @returns Promise that resolves when all keys are removed\n */\n protected async clearAll(): Promise<void> {\n return this.tools.store.clearAll();\n }\n\n /**\n * Queues a callback to execute in a separate worker context.\n *\n * @param callback - The callback token created with `this.callback()`\n * @param options - Optional configuration for the execution\n * @param options.runAt - If provided, schedules execution at this time; otherwise runs immediately\n * @returns Promise resolving to a cancellation token (only for scheduled executions)\n */\n protected async runTask(\n callback: Callback,\n options?: { runAt?: Date }\n ): Promise<string | void> {\n return this.tools.tasks.runTask(callback, options);\n }\n\n /**\n * Cancels a previously scheduled execution.\n *\n * @param token - The cancellation token returned by runTask() with runAt option\n * @returns Promise that resolves when the cancellation is processed\n */\n protected async cancelTask(token: string): Promise<void> {\n return this.tools.tasks.cancelTask(token);\n }\n\n /**\n * Cancels all scheduled executions for this twist.\n *\n * @returns Promise that resolves when all cancellations are processed\n */\n protected async cancelAllTasks(): Promise<void> {\n return this.tools.tasks.cancelAllTasks();\n }\n\n /**\n * Schedules a **singleton** task keyed by `key`: re-scheduling under the same\n * key atomically replaces any pending task, so at most one is ever live.\n *\n * Prefer this over `runTask({ runAt })` for recurring/self-renewing jobs\n * (watch renewals, polling, deferred cleanup) — it removes the error-prone\n * \"store token, cancel before re-scheduling\" bookkeeping that otherwise leaks\n * parallel task chains. See {@link Tasks.scheduleTask}.\n *\n * @param key - Stable identifier scoped to what the task renews\n * @param callback - The callback token created with `this.callback()`\n * @param options.runAt - When to run (required)\n * @returns Promise resolving to the scheduled task's cancellation token\n */\n protected async scheduleTask(\n key: string,\n callback: Callback,\n options: { runAt: Date }\n ): Promise<string | void> {\n return this.tools.tasks.scheduleTask(key, callback, options);\n }\n\n /**\n * Cancels the singleton task previously scheduled under `key` (if any).\n * No-op if none exists or it already ran. See {@link Tasks.cancelScheduledTask}.\n *\n * @param key - The same key passed to {@link scheduleTask}\n * @returns Promise that resolves when the cancellation is processed\n */\n protected async cancelScheduledTask(key: string): Promise<void> {\n return this.tools.tasks.cancelScheduledTask(key);\n }\n\n /**\n * Called when the twist is installed by a user.\n *\n * This method should contain initialization logic such as seeding\n * initial threads, configuring webhooks, or establishing external\n * connections. When it runs, `this.userId` is already populated with\n * the installing user's ID.\n *\n * @param context - Optional context containing the actor who triggered activation\n * @returns Promise that resolves when activation is complete\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n activate(context?: { actor: Actor }): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a new version of the twist is deployed.\n *\n * This method should contain migration logic for updating old data structures\n * or setting up new resources that weren't needed by the previous version.\n * It is called once per active twist_instance with the new version.\n *\n * @returns Promise that resolves when upgrade is complete\n */\n upgrade(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when the twist's options configuration changes.\n *\n * Override to react to option changes, e.g. archiving items when a sync\n * type is toggled off, or starting sync when a type is toggled on.\n *\n * @param oldOptions - The previously resolved options\n * @param newOptions - The newly resolved options\n * @returns Promise that resolves when the change is handled\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onOptionsChanged(\n oldOptions: Record<string, any>,\n newOptions: Record<string, any>\n ): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when the twist is uninstalled.\n *\n * This method should contain cleanup logic such as removing webhooks,\n * cleaning up external resources, or performing final data operations.\n *\n * @returns Promise that resolves when deactivation is complete\n */\n deactivate(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a thread created by this twist is updated.\n * Override to implement two-way sync with an external system.\n *\n * @param thread - The updated thread\n * @param changes - Tag additions and removals on the thread\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onThreadUpdated(\n thread: Thread,\n changes: {\n tagsAdded: Record<Tag, ActorId[]>;\n tagsRemoved: Record<Tag, ActorId[]>;\n }\n ): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a note is created on a thread created by this twist.\n * Override to implement two-way sync (e.g. syncing notes as comments).\n *\n * Notes created by the twist itself are filtered out to prevent loops.\n *\n * Returning a string sets the note's `key` for future upsert matching,\n * linking the Plot note to its external counterpart so that subsequent\n * syncs (reactions, edits) update the existing note instead of creating duplicates.\n *\n * @param note - The newly created note\n * @returns Optional note key for external deduplication\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onNoteCreated(note: Note, ...args: any[]): Promise<string | NoteWriteBackResult | void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a link is created in a connected source channel.\n * Requires `link: true` in Plot options.\n *\n * @param link - The newly created link\n * @param notes - Notes on the link's thread\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onLinkCreated(link: Link, notes: Note[]): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a link in a connected source channel is updated.\n * Requires `link: true` in Plot options.\n *\n * @param link - The updated link\n * @param notes - Notes on the link's thread (optional)\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onLinkUpdated(link: Link, notes?: Note[]): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a note is created on a thread with a link from a connected channel.\n * Requires `link: true` in Plot options.\n *\n * @param note - The newly created note\n * @param link - The link associated with the thread\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onLinkNoteCreated(note: Note, link: Link): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Waits for tool initialization to complete.\n * Called automatically by the entrypoint before lifecycle methods.\n * @internal\n */\n async waitForReady(): Promise<void> {\n await this.toolShed.waitForReady();\n }\n}\n";
7
+ export default "import type { NoteWriteBackResult } from \"./connector\";\nimport { type Action, type Actor, type ActorId, type Link, type Note, type Thread, Uuid } from \"./plot\";\nimport type { Tag } from \"./tag\";\nimport { type ITool } from \"./tool\";\nimport type { Callback } from \"./tools/callbacks\";\nimport type { Serializable } from \"./utils/serializable\";\nimport type { InferTools, ToolBuilder, ToolShed } from \"./utils/types\";\n\n/**\n * Base class for all twists.\n *\n * A twist is installed at the workspace level and is owned by a single user\n * (see `this.userId`). It has no inherent focus scope: threads, notes, and\n * links it creates are filed against the owner's focuses, with automatic\n * focus matching when no explicit target is provided.\n *\n * Override `build()` to declare tool dependencies and lifecycle methods to\n * handle events.\n *\n * @example\n * ```typescript\n * class FlatteringTwist extends Twist<FlatteringTwist> {\n * build(build: ToolBuilder) {\n * return {\n * plot: build(Plot),\n * };\n * }\n *\n * async activate() {\n * await this.tools.plot.createThread({\n * title: \"Hello, good looking!\",\n * });\n * }\n * }\n * ```\n */\nexport abstract class Twist<TSelf> {\n /**\n * When `true`, users may install multiple instances of this twist within\n * the same scope (personal workspace or team). Each instance must have a\n * distinct name.\n *\n * Defaults to `false` (single instance per scope).\n *\n * @example\n * ```typescript\n * class WorkflowTwist extends Twist<WorkflowTwist> {\n * static readonly multipleInstances = true;\n * // ...\n * }\n * ```\n */\n static readonly multipleInstances?: boolean;\n\n /**\n * The user ID (`twist_instance.owner_id`) that installed this twist.\n * Populated by the runtime before any lifecycle method runs.\n */\n protected userId!: Uuid;\n\n constructor(protected id: Uuid, private toolShed: ToolShed) {}\n\n /**\n * Gets the initialized tools for this twist.\n * @throws Error if called before initialization is complete\n */\n protected get tools(): InferTools<TSelf> {\n return this.toolShed.getTools<InferTools<TSelf>>();\n }\n\n /**\n * Declares tool dependencies for this twist.\n * Return an object mapping tool names to build() promises.\n *\n * @param build - The build function to use for declaring dependencies\n * @returns Object mapping tool names to tool promises\n *\n * @example\n * ```typescript\n * build(build: ToolBuilder) {\n * return {\n * plot: build(Plot),\n * calendar: build(GoogleCalendar, { apiKey: \"...\" }),\n * };\n * }\n * ```\n */\n abstract build(build: ToolBuilder): Record<string, Promise<ITool>>;\n\n /**\n * Creates a persistent callback to a method on this twist.\n *\n * ExtraArgs are strongly typed to match the method's signature. They must be serializable.\n *\n * @param fn - The method to callback\n * @param extraArgs - Additional arguments to pass (type-checked, must be serializable)\n * @returns Promise resolving to a persistent callback token\n *\n * @example\n * ```typescript\n * const callback = await this.callback(this.onWebhook, \"calendar\", 123);\n * ```\n */\n protected callback<\n TArgs extends Serializable[],\n Fn extends (...args: TArgs) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback>;\n // Overload when caller provides the first argument\n protected callback<\n TArgs extends Serializable[],\n Fn extends (arg1: any, ...extraArgs: TArgs) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback>;\n protected async callback<\n TArgs extends Serializable[],\n Fn extends (...args: any[]) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback> {\n return this.tools.callbacks.create(fn, ...extraArgs);\n }\n\n /**\n * Like callback(), but for an Action, which receives the action as the first argument.\n *\n * @param fn - The method to callback\n * @param extraArgs - Additional arguments to pass after the action\n * @returns Promise resolving to a persistent callback token\n *\n * @example\n * ```typescript\n * const callback = await this.actionCallback(this.doSomething, 123);\n * const action: Action = {\n * type: ActionType.callback,\n * title: \"Do Something\",\n * callback,\n * };\n * ```\n */\n protected async actionCallback<\n TArgs extends Serializable[],\n Fn extends (action: Action, ...extraArgs: TArgs) => any\n >(fn: Fn, ...extraArgs: TArgs): Promise<Callback> {\n return this.tools.callbacks.create(fn, ...extraArgs);\n }\n\n /**\n * Deletes a specific callback by its token.\n *\n * @param token - The callback token to delete\n * @returns Promise that resolves when the callback is deleted\n */\n protected async deleteCallback(token: Callback): Promise<void> {\n return this.tools.callbacks.delete(token);\n }\n\n /**\n * Deletes all callbacks for this twist.\n *\n * @returns Promise that resolves when all callbacks are deleted\n */\n protected async deleteAllCallbacks(): Promise<void> {\n return this.tools.callbacks.deleteAll();\n }\n\n /**\n * Executes a callback by its token inline in the current execution.\n *\n * **Use `this.runTask()` instead for batch continuations and long-running work.**\n * `this.run()` executes inline, sharing the current request count (~1000 limit)\n * and blocking the HTTP response. This causes timeouts when used in lifecycle\n * methods like `onChannelEnabled` or `syncBatch` continuations.\n *\n * `this.run()` is appropriate when you need the callback's **return value** —\n * e.g., running a parent callback token that returns data. For fire-and-forget\n * work, always prefer `this.runTask()`.\n *\n * @param token - The callback token to execute\n * @param args - Optional arguments to pass to the callback\n * @returns Promise resolving to the callback result\n */\n protected async run(token: Callback, ...args: []): Promise<any> {\n return this.tools.callbacks.run(token, ...args);\n }\n\n /**\n * Retrieves a value from persistent storage by key.\n *\n * Values are automatically deserialized using SuperJSON, which\n * properly restores Date objects, Maps, Sets, and other complex types.\n *\n * @template T - The expected type of the stored value (must be Serializable)\n * @param key - The storage key to retrieve\n * @returns Promise resolving to the stored value or null\n */\n protected async get<T extends import(\"./index\").Serializable>(\n key: string\n ): Promise<T | null> {\n return this.tools.store.get(key);\n }\n\n /**\n * Stores a value in persistent storage.\n *\n * The value will be serialized using SuperJSON and stored persistently.\n * SuperJSON automatically handles Date objects, Maps, Sets, undefined values,\n * and other complex types that standard JSON doesn't support.\n *\n * **Important**: Functions and Symbols cannot be stored.\n * **For function references**: Use callbacks instead of storing functions directly.\n *\n * @example\n * ```typescript\n * // ✅ Date objects are preserved\n * await this.set(\"sync_state\", {\n * lastSync: new Date(),\n * minDate: new Date(2024, 0, 1)\n * });\n *\n * // ✅ undefined is now supported\n * await this.set(\"data\", { name: \"test\", optional: undefined });\n *\n * // ❌ WRONG: Cannot store functions directly\n * await this.set(\"handler\", this.myHandler);\n *\n * // ✅ CORRECT: Create a callback token first\n * const token = await this.callback(this.myHandler, \"arg1\", \"arg2\");\n * await this.set(\"handler_token\", token);\n *\n * // Later, execute the callback\n * const token = await this.get<Callback>(\"handler_token\");\n * await this.run(token);\n * ```\n *\n * @template T - The type of value being stored (must be Serializable)\n * @param key - The storage key to use\n * @param value - The value to store (must be SuperJSON-serializable)\n * @returns Promise that resolves when the value is stored\n */\n protected async set<T extends import(\"./index\").Serializable>(\n key: string,\n value: T\n ): Promise<void> {\n return this.tools.store.set(key, value);\n }\n\n /**\n * Removes a specific key from persistent storage.\n *\n * @param key - The storage key to remove\n * @returns Promise that resolves when the key is removed\n */\n protected async clear(key: string): Promise<void> {\n return this.tools.store.clear(key);\n }\n\n /**\n * Removes all keys from this twist's storage.\n *\n * @returns Promise that resolves when all keys are removed\n */\n protected async clearAll(): Promise<void> {\n return this.tools.store.clearAll();\n }\n\n /**\n * Queues a callback to execute in a separate worker context.\n *\n * @param callback - The callback token created with `this.callback()`\n * @param options - Optional configuration for the execution\n * @param options.runAt - If provided, schedules execution at this time; otherwise runs immediately\n * @returns Promise resolving to a cancellation token (only for scheduled executions)\n */\n protected async runTask(\n callback: Callback,\n options?: { runAt?: Date }\n ): Promise<string | void> {\n return this.tools.tasks.runTask(callback, options);\n }\n\n /**\n * Cancels a previously scheduled execution.\n *\n * @param token - The cancellation token returned by runTask() with runAt option\n * @returns Promise that resolves when the cancellation is processed\n */\n protected async cancelTask(token: string): Promise<void> {\n return this.tools.tasks.cancelTask(token);\n }\n\n /**\n * Cancels all scheduled executions for this twist.\n *\n * @returns Promise that resolves when all cancellations are processed\n */\n protected async cancelAllTasks(): Promise<void> {\n return this.tools.tasks.cancelAllTasks();\n }\n\n /**\n * Schedules a **singleton** task keyed by `key`: re-scheduling under the same\n * key atomically replaces any pending task, so at most one is ever live.\n *\n * Prefer this over `runTask({ runAt })` for recurring/self-renewing jobs\n * (watch renewals, polling, deferred cleanup) — it removes the error-prone\n * \"store token, cancel before re-scheduling\" bookkeeping that otherwise leaks\n * parallel task chains. See {@link Tasks.scheduleTask}.\n *\n * @param key - Stable identifier scoped to what the task renews\n * @param callback - The callback token created with `this.callback()`\n * @param options.runAt - When to run (required)\n * @returns Promise resolving to the scheduled task's cancellation token\n */\n protected async scheduleTask(\n key: string,\n callback: Callback,\n options: { runAt: Date }\n ): Promise<string | void> {\n return this.tools.tasks.scheduleTask(key, callback, options);\n }\n\n /**\n * Cancels the singleton task previously scheduled under `key` (if any).\n * No-op if none exists or it already ran. See {@link Tasks.cancelScheduledTask}.\n *\n * @param key - The same key passed to {@link scheduleTask}\n * @returns Promise that resolves when the cancellation is processed\n */\n protected async cancelScheduledTask(key: string): Promise<void> {\n return this.tools.tasks.cancelScheduledTask(key);\n }\n\n /**\n * Schedules a durable recurring task under a stable key. The platform\n * re-arms the task every `intervalMs` automatically — the callback does NOT\n * need to reschedule itself. Re-scheduling under the same key atomically\n * replaces the pending occurrence (at most one live task per key). Tear down\n * with {@link cancelScheduledTask}. See {@link Tasks.scheduleRecurring}.\n *\n * @param key - Stable identifier, e.g. `\"mailbox-self-heal\"`\n * @param callback - Callback token created with `this.callback()`\n * @param options.intervalMs - Safety-ceiling cadence in milliseconds\n * @param options.firstRunAt - Optional precise time for the next fire\n */\n protected async scheduleRecurring(\n key: string,\n callback: Callback,\n options: { intervalMs: number; firstRunAt?: Date }\n ): Promise<void> {\n return this.tools.tasks.scheduleRecurring(key, callback, options);\n }\n\n /**\n * Called when the twist is installed by a user.\n *\n * This method should contain initialization logic such as seeding\n * initial threads, configuring webhooks, or establishing external\n * connections. When it runs, `this.userId` is already populated with\n * the installing user's ID.\n *\n * @param context - Optional context containing the actor who triggered activation\n * @returns Promise that resolves when activation is complete\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n activate(context?: { actor: Actor }): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a new version of the twist is deployed.\n *\n * This method should contain migration logic for updating old data structures\n * or setting up new resources that weren't needed by the previous version.\n * It is called once per active twist_instance with the new version.\n *\n * @returns Promise that resolves when upgrade is complete\n */\n upgrade(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when the twist's options configuration changes.\n *\n * Override to react to option changes, e.g. archiving items when a sync\n * type is toggled off, or starting sync when a type is toggled on.\n *\n * @param oldOptions - The previously resolved options\n * @param newOptions - The newly resolved options\n * @returns Promise that resolves when the change is handled\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onOptionsChanged(\n oldOptions: Record<string, any>,\n newOptions: Record<string, any>\n ): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when the twist is uninstalled.\n *\n * This method should contain cleanup logic such as removing webhooks,\n * cleaning up external resources, or performing final data operations.\n *\n * @returns Promise that resolves when deactivation is complete\n */\n deactivate(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a thread created by this twist is updated.\n * Override to implement two-way sync with an external system.\n *\n * @param thread - The updated thread\n * @param changes - Tag additions and removals on the thread\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onThreadUpdated(\n thread: Thread,\n changes: {\n tagsAdded: Record<Tag, ActorId[]>;\n tagsRemoved: Record<Tag, ActorId[]>;\n }\n ): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a note is created on a thread created by this twist.\n * Override to implement two-way sync (e.g. syncing notes as comments).\n *\n * Notes created by the twist itself are filtered out to prevent loops.\n *\n * Returning a string sets the note's `key` for future upsert matching,\n * linking the Plot note to its external counterpart so that subsequent\n * syncs (reactions, edits) update the existing note instead of creating duplicates.\n *\n * @param note - The newly created note\n * @returns Optional note key for external deduplication\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onNoteCreated(note: Note, ...args: any[]): Promise<string | NoteWriteBackResult | void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a link is created in a connected source channel.\n * Requires `link: true` in Plot options.\n *\n * @param link - The newly created link\n * @param notes - Notes on the link's thread\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onLinkCreated(link: Link, notes: Note[]): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a link in a connected source channel is updated.\n * Requires `link: true` in Plot options.\n *\n * @param link - The updated link\n * @param notes - Notes on the link's thread (optional)\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onLinkUpdated(link: Link, notes?: Note[]): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Called when a note is created on a thread with a link from a connected channel.\n * Requires `link: true` in Plot options.\n *\n * @param note - The newly created note\n * @param link - The link associated with the thread\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n onLinkNoteCreated(note: Note, link: Link): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Waits for tool initialization to complete.\n * Called automatically by the entrypoint before lifecycle methods.\n * @internal\n */\n async waitForReady(): Promise<void> {\n await this.toolShed.waitForReady();\n }\n}\n";
8
8
  //# sourceMappingURL=twist.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"twist.js","sourceRoot":"","sources":["../../src/llm-docs/twist.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAe,25fAA25f,CAAC"}
1
+ {"version":3,"file":"twist.js","sourceRoot":"","sources":["../../src/llm-docs/twist.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAe,8zhBAA8zhB,CAAC"}
package/dist/tool.d.ts CHANGED
@@ -250,6 +250,22 @@ export declare abstract class Tool<TSelf> implements ITool {
250
250
  * @returns Promise that resolves when the cancellation is processed
251
251
  */
252
252
  protected cancelScheduledTask(key: string): Promise<void>;
253
+ /**
254
+ * Schedules a durable recurring task under a stable key. The platform
255
+ * re-arms the task every `intervalMs` automatically — the callback does NOT
256
+ * need to reschedule itself. Re-scheduling under the same key atomically
257
+ * replaces the pending occurrence (at most one live task per key). Tear down
258
+ * with {@link cancelScheduledTask}. See {@link Tasks.scheduleRecurring}.
259
+ *
260
+ * @param key - Stable identifier, e.g. `"mailbox-self-heal"`
261
+ * @param callback - Callback token created with `this.callback()`
262
+ * @param options.intervalMs - Safety-ceiling cadence in milliseconds
263
+ * @param options.firstRunAt - Optional precise time for the next fire
264
+ */
265
+ protected scheduleRecurring(key: string, callback: Callback, options: {
266
+ intervalMs: number;
267
+ firstRunAt?: Date;
268
+ }): Promise<void>;
253
269
  /**
254
270
  * Called before the twist's activate method, starting from the deepest tool dependencies.
255
271
  *
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../src/tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../src/tool.ts"],"names":[],"mappings":"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"}
package/dist/tool.js CHANGED
@@ -279,6 +279,21 @@ export class Tool {
279
279
  async cancelScheduledTask(key) {
280
280
  return this.tools.tasks.cancelScheduledTask(key);
281
281
  }
282
+ /**
283
+ * Schedules a durable recurring task under a stable key. The platform
284
+ * re-arms the task every `intervalMs` automatically — the callback does NOT
285
+ * need to reschedule itself. Re-scheduling under the same key atomically
286
+ * replaces the pending occurrence (at most one live task per key). Tear down
287
+ * with {@link cancelScheduledTask}. See {@link Tasks.scheduleRecurring}.
288
+ *
289
+ * @param key - Stable identifier, e.g. `"mailbox-self-heal"`
290
+ * @param callback - Callback token created with `this.callback()`
291
+ * @param options.intervalMs - Safety-ceiling cadence in milliseconds
292
+ * @param options.firstRunAt - Optional precise time for the next fire
293
+ */
294
+ async scheduleRecurring(key, callback, options) {
295
+ return this.tools.tasks.scheduleRecurring(key, callback, options);
296
+ }
282
297
  /**
283
298
  * Called before the twist's activate method, starting from the deepest tool dependencies.
284
299
  *
package/dist/tool.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tool.js","sourceRoot":"","sources":["../src/tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tool.js","sourceRoot":"","sources":["../src/tool.ts"],"names":[],"mappings":"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"}
@@ -127,18 +127,19 @@ export declare abstract class Tasks extends ITool {
127
127
  */
128
128
  abstract cancelAllTasks(): Promise<void>;
129
129
  /**
130
- * Schedules a **singleton** task identified by `key`: scheduling under a key
131
- * that already has a pending task atomically cancels the existing one and
132
- * replaces it. At most one scheduled task per `key` is ever live.
133
- *
134
- * Use this for any recurring/self-renewing jobwebhook/watch renewals,
135
- * periodic polling, deferred cleanup instead of hand-managing tokens with
136
- * `runTask()` + `cancelTask()`. The manual pattern (store the token, cancel
137
- * it before re-scheduling) is easy to get wrong: a renewal callback that
138
- * re-schedules itself, combined with any *extra* scheduling call (a
139
- * re-dispatched `onChannelEnabled`, a re-init), leaks parallel self-
140
- * perpetuating chains that accumulate forever and can trip the runtime's
141
- * execution quota. Keying makes that leak impossible by construction.
130
+ * Schedules a **one-shot singleton** task identified by `key`: scheduling
131
+ * under a key that already has a pending task atomically cancels the existing
132
+ * one and replaces it. At most one scheduled task per `key` is ever live.
133
+ *
134
+ * Use this for **one-shot keyed deferred work** a single future task whose
135
+ * pending occurrence should be atomically replaced if re-scheduled (e.g.
136
+ * a deferred cleanup, a one-time expiry action, a single future send).
137
+ *
138
+ * For **recurring/self-renewing jobs** (watch renewals, polling loops,
139
+ * periodic syncs, self-heal checks), use {@link scheduleRecurring} instead.
140
+ * It owns the cadence on the platform side, so the chain survives dropped
141
+ * runs, suspensions, and deploys without the callback needing to reschedule
142
+ * itself.
142
143
  *
143
144
  * Replacement is atomic on the server, so concurrent executions racing to
144
145
  * schedule the same key converge on a single task rather than leaking.
@@ -172,5 +173,50 @@ export declare abstract class Tasks extends ITool {
172
173
  * @returns Promise that resolves when the cancellation is processed
173
174
  */
174
175
  abstract cancelScheduledTask(key: string): Promise<void>;
176
+ /**
177
+ * Schedules a **durable recurring** task identified by `key`. Unlike
178
+ * {@link scheduleTask} (one-shot, deleted when it fires), a recurring task's
179
+ * next occurrence is owned by the platform: the runtime re-arms it every
180
+ * `intervalMs` automatically, so the chain survives a dropped queue message,
181
+ * a suspension, a deploy/eviction, or a callback that throws before it could
182
+ * reschedule. The callback just does the work, idempotently — it does NOT
183
+ * need to reschedule itself.
184
+ *
185
+ * `intervalMs` is a **safety ceiling** (the maximum gap between fires). For
186
+ * data-dependent cadence (e.g. renew 24h before a provider-returned expiry),
187
+ * pass `firstRunAt` for the precise next fire and re-call `scheduleRecurring`
188
+ * with the same key on each run to keep tightening it; the ceiling guarantees
189
+ * the chain still fires if a run is lost. `firstRunAt` can pull the next fire
190
+ * earlier than the ceiling but never later.
191
+ *
192
+ * Recurring tasks are keyed/singleton: re-scheduling under the same key
193
+ * atomically replaces the pending occurrence (one live task per key). Tear
194
+ * down with {@link cancelScheduledTask}.
195
+ *
196
+ * @param key - Stable identifier, scoped to what it maintains, e.g.
197
+ * `` `watch-renewal:${folderId}` `` or `"mailbox-self-heal"`.
198
+ * @param callback - Callback created with `this.callback()`.
199
+ * @param options.intervalMs - Safety-ceiling cadence in milliseconds.
200
+ * @param options.firstRunAt - Optional precise time for the next fire
201
+ * (clamped to no later than now + intervalMs).
202
+ *
203
+ * @example
204
+ * ```typescript
205
+ * // Fixed cadence (self-heal, polling): register once, never reschedule.
206
+ * const cb = await this.callback(this.selfHealCheck);
207
+ * await this.scheduleRecurring("mailbox-self-heal", cb, { intervalMs: 60 * 60 * 1000 });
208
+ *
209
+ * // Variable cadence (watch renewal): precise firstRunAt + safety ceiling.
210
+ * const renew = await this.callback(this.renewWatch, folderId);
211
+ * await this.scheduleRecurring(`watch-renewal:${folderId}`, renew, {
212
+ * intervalMs: 3.5 * 24 * 60 * 60 * 1000, // ceiling: half the 7-day watch
213
+ * firstRunAt: new Date(expiry.getTime() - 24 * 60 * 60 * 1000),
214
+ * });
215
+ * ```
216
+ */
217
+ abstract scheduleRecurring(key: string, callback: Callback, options: {
218
+ intervalMs: number;
219
+ firstRunAt?: Date;
220
+ }): Promise<void>;
175
221
  }
176
222
  //# sourceMappingURL=tasks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/tools/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,CAAC;AAC3B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2EG;AACH,8BAAsB,KAAM,SAAQ,KAAK;IACvC;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IAEH,QAAQ,CAAC,OAAO,CACd,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,IAAI,CAAA;KAAE,GACzB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAEzB;;;;;;;;OAQG;IAEH,QAAQ,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEjD;;;;;;;OAOG;IACH,QAAQ,CAAC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IAEH,QAAQ,CAAC,YAAY,CACnB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE;QAAE,KAAK,EAAE,IAAI,CAAA;KAAE,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAEzB;;;;;;;;;OASG;IAEH,QAAQ,CAAC,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CACzD"}
1
+ {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/tools/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,CAAC;AAC3B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2EG;AACH,8BAAsB,KAAM,SAAQ,KAAK;IACvC;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IAEH,QAAQ,CAAC,OAAO,CACd,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,IAAI,CAAA;KAAE,GACzB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAEzB;;;;;;;;OAQG;IAEH,QAAQ,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEjD;;;;;;;OAOG;IACH,QAAQ,CAAC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IAEH,QAAQ,CAAC,YAAY,CACnB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE;QAAE,KAAK,EAAE,IAAI,CAAA;KAAE,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAEzB;;;;;;;;;OASG;IAEH,QAAQ,CAAC,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAExD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwCG;IAEH,QAAQ,CAAC,iBAAiB,CACxB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,IAAI,CAAA;KAAE,GACjD,OAAO,CAAC,IAAI,CAAC;CACjB"}
@@ -1 +1 @@
1
- {"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/tools/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,CAAC;AAG3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2EG;AACH,MAAM,OAAgB,KAAM,SAAQ,KAAK;CA2GxC"}
1
+ {"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/tools/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,CAAC;AAG3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2EG;AACH,MAAM,OAAgB,KAAM,SAAQ,KAAK;CA4JxC"}
package/dist/twist.d.ts CHANGED
@@ -257,6 +257,22 @@ export declare abstract class Twist<TSelf> {
257
257
  * @returns Promise that resolves when the cancellation is processed
258
258
  */
259
259
  protected cancelScheduledTask(key: string): Promise<void>;
260
+ /**
261
+ * Schedules a durable recurring task under a stable key. The platform
262
+ * re-arms the task every `intervalMs` automatically — the callback does NOT
263
+ * need to reschedule itself. Re-scheduling under the same key atomically
264
+ * replaces the pending occurrence (at most one live task per key). Tear down
265
+ * with {@link cancelScheduledTask}. See {@link Tasks.scheduleRecurring}.
266
+ *
267
+ * @param key - Stable identifier, e.g. `"mailbox-self-heal"`
268
+ * @param callback - Callback token created with `this.callback()`
269
+ * @param options.intervalMs - Safety-ceiling cadence in milliseconds
270
+ * @param options.firstRunAt - Optional precise time for the next fire
271
+ */
272
+ protected scheduleRecurring(key: string, callback: Callback, options: {
273
+ intervalMs: number;
274
+ firstRunAt?: Date;
275
+ }): Promise<void>;
260
276
  /**
261
277
  * Called when the twist is installed by a user.
262
278
  *