@elizaos/plugin-farcaster 2.0.0-alpha.8 → 2.0.0-beta.1

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 (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +108 -0
  3. package/auto-enable.ts +21 -0
  4. package/dist/actions/index.d.ts +2 -0
  5. package/dist/actions/index.d.ts.map +1 -0
  6. package/dist/auto-enable.d.ts +4 -0
  7. package/dist/auto-enable.d.ts.map +1 -0
  8. package/dist/browser/index.d.ts +2 -2
  9. package/dist/cjs/index.d.ts +2 -2
  10. package/dist/cjs/index.node.cjs +870 -719
  11. package/dist/cjs/index.node.js.map +23 -26
  12. package/dist/client/FarcasterClient.d.ts +41 -0
  13. package/dist/client/FarcasterClient.d.ts.map +1 -0
  14. package/dist/client/index.d.ts +2 -0
  15. package/dist/client/index.d.ts.map +1 -0
  16. package/dist/connector-account-provider.d.ts +16 -0
  17. package/dist/connector-account-provider.d.ts.map +1 -0
  18. package/dist/generated/specs/spec-helpers.d.ts +36 -0
  19. package/dist/generated/specs/spec-helpers.d.ts.map +1 -0
  20. package/dist/generated/specs/specs.d.ts +48 -0
  21. package/dist/generated/specs/specs.d.ts.map +1 -0
  22. package/dist/index.browser.d.ts +11 -0
  23. package/dist/index.browser.d.ts.map +1 -0
  24. package/dist/index.d.ts +10 -2
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.node.d.ts +3 -0
  27. package/dist/index.node.d.ts.map +1 -0
  28. package/dist/managers/AgentManager.d.ts +20 -0
  29. package/dist/managers/AgentManager.d.ts.map +1 -0
  30. package/dist/managers/CastManager.d.ts +25 -0
  31. package/dist/managers/CastManager.d.ts.map +1 -0
  32. package/dist/managers/EmbedManager.d.ts +40 -0
  33. package/dist/managers/EmbedManager.d.ts.map +1 -0
  34. package/dist/managers/InteractionManager.d.ts +32 -0
  35. package/dist/managers/InteractionManager.d.ts.map +1 -0
  36. package/dist/managers/InteractionProcessor.d.ts +10 -0
  37. package/dist/managers/InteractionProcessor.d.ts.map +1 -0
  38. package/dist/managers/InteractionSource.d.ts +38 -0
  39. package/dist/managers/InteractionSource.d.ts.map +1 -0
  40. package/dist/managers/index.d.ts +7 -0
  41. package/dist/managers/index.d.ts.map +1 -0
  42. package/dist/node/index.d.ts +2 -2
  43. package/dist/node/index.node.d.ts +2 -0
  44. package/dist/node/index.node.js +830 -704
  45. package/dist/node/index.node.js.map +23 -26
  46. package/dist/providers/index.d.ts +4 -0
  47. package/dist/providers/index.d.ts.map +1 -0
  48. package/dist/providers/profileProvider.d.ts +3 -0
  49. package/dist/providers/profileProvider.d.ts.map +1 -0
  50. package/dist/routes/webhook.d.ts +3 -0
  51. package/dist/routes/webhook.d.ts.map +1 -0
  52. package/dist/services/CastService.d.ts +133 -0
  53. package/dist/services/CastService.d.ts.map +1 -0
  54. package/dist/services/FarcasterService.d.ts +32 -0
  55. package/dist/services/FarcasterService.d.ts.map +1 -0
  56. package/dist/services/MessageService.d.ts +54 -0
  57. package/dist/services/MessageService.d.ts.map +1 -0
  58. package/dist/services/index.d.ts +4 -0
  59. package/dist/services/index.d.ts.map +1 -0
  60. package/dist/types/index.d.ts +119 -0
  61. package/dist/types/index.d.ts.map +1 -0
  62. package/dist/utils/asyncqueue.d.ts +13 -0
  63. package/dist/utils/asyncqueue.d.ts.map +1 -0
  64. package/dist/utils/callbacks.d.ts +14 -0
  65. package/dist/utils/callbacks.d.ts.map +1 -0
  66. package/dist/utils/config.d.ts +14 -0
  67. package/dist/utils/config.d.ts.map +1 -0
  68. package/dist/utils/index.d.ts +25 -0
  69. package/dist/utils/index.d.ts.map +1 -0
  70. package/dist/utils/prompts.d.ts +5 -0
  71. package/dist/utils/prompts.d.ts.map +1 -0
  72. package/dist/workflow-credential-provider.d.ts +21 -0
  73. package/dist/workflow-credential-provider.d.ts.map +1 -0
  74. package/package.json +20 -9
@@ -1,397 +1,123 @@
1
+ var __create = Object.create;
2
+ var __getProtoOf = Object.getPrototypeOf;
1
3
  var __defProp = Object.defineProperty;
2
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __moduleCache = /* @__PURE__ */ new WeakMap;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
12
+ var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
20
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
21
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
+ for (let key of __getOwnPropNames(mod))
23
+ if (!__hasOwnProp.call(to, key))
24
+ __defProp(to, key, {
25
+ get: __accessProp.bind(mod, key),
26
+ enumerable: true
27
+ });
28
+ if (canCache)
29
+ cache.set(mod, to);
30
+ return to;
31
+ };
6
32
  var __toCommonJS = (from) => {
7
- var entry = __moduleCache.get(from), desc;
33
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
8
34
  if (entry)
9
35
  return entry;
10
36
  entry = __defProp({}, "__esModule", { value: true });
11
- if (from && typeof from === "object" || typeof from === "function")
12
- __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
13
- get: () => from[key],
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- }));
37
+ if (from && typeof from === "object" || typeof from === "function") {
38
+ for (var key of __getOwnPropNames(from))
39
+ if (!__hasOwnProp.call(entry, key))
40
+ __defProp(entry, key, {
41
+ get: __accessProp.bind(from, key),
42
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
43
+ });
44
+ }
16
45
  __moduleCache.set(from, entry);
17
46
  return entry;
18
47
  };
48
+ var __moduleCache;
49
+ var __returnValue = (v) => v;
50
+ function __exportSetter(name, newValue) {
51
+ this[name] = __returnValue.bind(null, newValue);
52
+ }
19
53
  var __export = (target, all) => {
20
54
  for (var name in all)
21
55
  __defProp(target, name, {
22
56
  get: all[name],
23
57
  enumerable: true,
24
58
  configurable: true,
25
- set: (newValue) => all[name] = () => newValue
59
+ set: __exportSetter.bind(all, name)
26
60
  });
27
61
  };
28
62
 
29
63
  // index.node.ts
30
64
  var exports_index_node = {};
31
65
  __export(exports_index_node, {
66
+ resolveDefaultFarcasterAccountId: () => resolveDefaultFarcasterAccountId,
67
+ readFarcasterAccountId: () => readFarcasterAccountId,
68
+ normalizeFarcasterAccountId: () => normalizeFarcasterAccountId,
69
+ listFarcasterAccountIds: () => listFarcasterAccountIds,
32
70
  isEmbedUrl: () => isEmbedUrl,
33
71
  isEmbedCast: () => isEmbedCast,
34
72
  farcasterPlugin: () => farcasterPlugin,
35
- default: () => typescript_default,
73
+ default: () => import__2.default,
74
+ createFarcasterConnectorAccountProvider: () => createFarcasterConnectorAccountProvider,
36
75
  FarcasterService: () => FarcasterService,
37
76
  FarcasterClient: () => FarcasterClient,
38
- EmbedManager: () => EmbedManager
77
+ FARCASTER_PROVIDER_ID: () => FARCASTER_PROVIDER_ID,
78
+ EmbedManager: () => EmbedManager,
79
+ DEFAULT_FARCASTER_ACCOUNT_ID: () => DEFAULT_FARCASTER_ACCOUNT_ID
39
80
  });
40
81
  module.exports = __toCommonJS(exports_index_node);
41
82
 
42
- // actions/replyCast.ts
43
- var import_core = require("@elizaos/core");
44
-
45
- // generated/specs/specs.ts
46
- var coreActionsSpec = {
47
- version: "1.0.0",
48
- actions: [
49
- {
50
- name: "SEND_CAST",
51
- description: "Posts a cast (message) on Farcaster",
52
- similes: ["POST_CAST", "FARCASTER_POST", "CAST", "SHARE_ON_FARCASTER", "ANNOUNCE"],
53
- parameters: []
54
- },
55
- {
56
- name: "REPLY_TO_CAST",
57
- description: "Replies to a cast on Farcaster",
58
- similes: ["REPLY_CAST", "RESPOND_CAST", "ANSWER_CAST", "COMMENT_CAST"],
59
- parameters: []
60
- }
61
- ]
62
- };
63
- var allActionsSpec = {
64
- version: "1.0.0",
65
- actions: [
66
- {
67
- name: "SEND_CAST",
68
- description: "Posts a cast (message) on Farcaster",
69
- similes: ["POST_CAST", "FARCASTER_POST", "CAST", "SHARE_ON_FARCASTER", "ANNOUNCE"],
70
- parameters: []
71
- },
72
- {
73
- name: "REPLY_TO_CAST",
74
- description: "Replies to a cast on Farcaster",
75
- similes: ["REPLY_CAST", "RESPOND_CAST", "ANSWER_CAST", "COMMENT_CAST"],
76
- parameters: []
77
- }
78
- ]
79
- };
80
- var coreProvidersSpec = {
81
- version: "1.0.0",
82
- providers: [
83
- {
84
- name: "farcasterProfile",
85
- description: "Provides information about the agent",
86
- dynamic: true
87
- },
88
- {
89
- name: "farcasterThread",
90
- description: "Provides thread context for Farcaster casts so the agent can reference the full conversation.",
91
- dynamic: true
92
- },
93
- {
94
- name: "farcasterTimeline",
95
- description: "Provides recent casts from the agent",
96
- dynamic: true
97
- }
98
- ]
99
- };
100
- var allProvidersSpec = {
101
- version: "1.0.0",
102
- providers: [
103
- {
104
- name: "farcasterProfile",
105
- description: "Provides information about the agent",
106
- dynamic: true
107
- },
108
- {
109
- name: "farcasterThread",
110
- description: "Provides thread context for Farcaster casts so the agent can reference the full conversation.",
111
- dynamic: true
112
- },
113
- {
114
- name: "farcasterTimeline",
115
- description: "Provides recent casts from the agent",
116
- dynamic: true
117
- }
118
- ]
119
- };
120
- var coreEvaluatorsSpec = {
121
- version: "1.0.0",
122
- evaluators: []
123
- };
124
- var allEvaluatorsSpec = {
125
- version: "1.0.0",
126
- evaluators: []
127
- };
128
- var coreActionDocs = coreActionsSpec.actions;
129
- var allActionDocs = allActionsSpec.actions;
130
- var coreProviderDocs = coreProvidersSpec.providers;
131
- var allProviderDocs = allProvidersSpec.providers;
132
- var coreEvaluatorDocs = coreEvaluatorsSpec.evaluators;
133
- var allEvaluatorDocs = allEvaluatorsSpec.evaluators;
83
+ // index.ts
84
+ var import_core11 = require("@elizaos/core");
134
85
 
135
- // generated/specs/spec-helpers.ts
136
- var coreActionMap = new Map(coreActionDocs.map((doc) => [doc.name, doc]));
137
- var allActionMap = new Map(allActionDocs.map((doc) => [doc.name, doc]));
138
- var coreProviderMap = new Map(coreProviderDocs.map((doc) => [doc.name, doc]));
139
- var allProviderMap = new Map(allProviderDocs.map((doc) => [doc.name, doc]));
140
- var coreEvaluatorMap = new Map(coreEvaluatorDocs.map((doc) => [doc.name, doc]));
141
- var allEvaluatorMap = new Map(allEvaluatorDocs.map((doc) => [doc.name, doc]));
142
- function getActionSpec(name) {
143
- return coreActionMap.get(name) ?? allActionMap.get(name);
144
- }
145
- function requireActionSpec(name) {
146
- const spec = getActionSpec(name);
147
- if (!spec) {
148
- throw new Error(`Action spec not found: ${name}`);
149
- }
150
- return spec;
151
- }
152
- function getProviderSpec(name) {
153
- return coreProviderMap.get(name) ?? allProviderMap.get(name);
154
- }
155
- function requireProviderSpec(name) {
156
- const spec = getProviderSpec(name);
157
- if (!spec) {
158
- throw new Error(`Provider spec not found: ${name}`);
159
- }
160
- return spec;
161
- }
86
+ // utils/config.ts
87
+ var import_core = require("@elizaos/core");
88
+ var import_zod = require("zod");
162
89
 
163
90
  // types/index.ts
164
- var import_zod = require("zod");
91
+ var zod = __toESM(require("zod"));
92
+ var z2 = zod.z ?? zod;
165
93
  var DEFAULT_MAX_CAST_LENGTH = 320;
166
94
  var DEFAULT_POLL_INTERVAL = 120;
167
95
  var DEFAULT_CAST_INTERVAL_MIN = 90;
168
96
  var DEFAULT_CAST_INTERVAL_MAX = 180;
169
97
  var DEFAULT_CAST_CACHE_TTL = 1000 * 30 * 60;
170
98
  var DEFAULT_CAST_CACHE_SIZE = 9000;
171
- var FarcasterConfigSchema = import_zod.z.object({
172
- FARCASTER_DRY_RUN: import_zod.z.union([import_zod.z.boolean(), import_zod.z.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
173
- FARCASTER_FID: import_zod.z.number().int().min(1, "Farcaster fid is required"),
174
- MAX_CAST_LENGTH: import_zod.z.number().int().default(DEFAULT_MAX_CAST_LENGTH),
175
- FARCASTER_POLL_INTERVAL: import_zod.z.number().int().default(DEFAULT_POLL_INTERVAL),
176
- FARCASTER_MODE: import_zod.z.enum(["polling", "webhook"]).default("polling"),
177
- ENABLE_CAST: import_zod.z.union([import_zod.z.boolean(), import_zod.z.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
178
- CAST_INTERVAL_MIN: import_zod.z.number().int(),
179
- CAST_INTERVAL_MAX: import_zod.z.number().int(),
180
- ENABLE_ACTION_PROCESSING: import_zod.z.union([import_zod.z.boolean(), import_zod.z.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
181
- ACTION_INTERVAL: import_zod.z.number().int(),
182
- CAST_IMMEDIATELY: import_zod.z.union([import_zod.z.boolean(), import_zod.z.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
183
- MAX_ACTIONS_PROCESSING: import_zod.z.number().int(),
184
- FARCASTER_SIGNER_UUID: import_zod.z.string().min(1, "FARCASTER_SIGNER_UUID is not set"),
185
- FARCASTER_NEYNAR_API_KEY: import_zod.z.string().min(1, "FARCASTER_NEYNAR_API_KEY is not set"),
186
- FARCASTER_HUB_URL: import_zod.z.string().min(1, "FARCASTER_HUB_URL is not set")
99
+ var FarcasterConfigSchema = z2.object({
100
+ FARCASTER_DRY_RUN: z2.union([z2.boolean(), z2.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
101
+ FARCASTER_FID: z2.number().int().min(1, "Farcaster fid is required"),
102
+ MAX_CAST_LENGTH: z2.number().int().default(DEFAULT_MAX_CAST_LENGTH),
103
+ FARCASTER_POLL_INTERVAL: z2.number().int().default(DEFAULT_POLL_INTERVAL),
104
+ FARCASTER_MODE: z2.enum(["polling", "webhook"]).default("polling"),
105
+ ENABLE_CAST: z2.union([z2.boolean(), z2.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
106
+ CAST_INTERVAL_MIN: z2.number().int(),
107
+ CAST_INTERVAL_MAX: z2.number().int(),
108
+ ENABLE_ACTION_PROCESSING: z2.union([z2.boolean(), z2.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
109
+ ACTION_INTERVAL: z2.number().int(),
110
+ CAST_IMMEDIATELY: z2.union([z2.boolean(), z2.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
111
+ MAX_ACTIONS_PROCESSING: z2.number().int(),
112
+ FARCASTER_SIGNER_UUID: z2.string().min(1, "FARCASTER_SIGNER_UUID is not set"),
113
+ FARCASTER_NEYNAR_API_KEY: z2.string().min(1, "FARCASTER_NEYNAR_API_KEY is not set"),
114
+ FARCASTER_HUB_URL: z2.string().min(1, "FARCASTER_HUB_URL is not set")
187
115
  });
188
116
  var FARCASTER_SERVICE_NAME = "farcaster";
189
117
  var FARCASTER_SOURCE = "farcaster";
190
118
 
191
- // actions/replyCast.ts
192
- var spec = requireActionSpec("REPLY_TO_CAST");
193
- var replyCastAction = {
194
- name: spec.name,
195
- description: spec.description,
196
- examples: [
197
- [
198
- {
199
- name: "user",
200
- content: {
201
- text: "Someone asked about ElizaOS on Farcaster, can you reply?"
202
- }
203
- },
204
- {
205
- name: "assistant",
206
- content: {
207
- text: "I'll reply to their question about ElizaOS.",
208
- actions: ["REPLY_TO_CAST"]
209
- }
210
- }
211
- ],
212
- [
213
- {
214
- name: "user",
215
- content: { text: "Reply to that cast and thank them for the feedback" }
216
- },
217
- {
218
- name: "assistant",
219
- content: {
220
- text: "I'll reply with a thank you message.",
221
- actions: ["REPLY_TO_CAST"]
222
- }
223
- }
224
- ]
225
- ],
226
- validate: async (runtime, message, state, options) => {
227
- const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
228
- const __avText = __avTextRaw.toLowerCase();
229
- const __avKeywords = ["reply", "cast"];
230
- const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
231
- const __avRegex = /\b(?:reply|cast)\b/i;
232
- const __avRegexOk = Boolean(__avText.match(__avRegex));
233
- const __avSource = String(message?.content?.source ?? message?.source ?? "");
234
- const __avExpectedSource = "farcaster";
235
- const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
236
- const __avOptions = options && typeof options === "object" ? options : {};
237
- const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
238
- if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
239
- return false;
240
- }
241
- const __avLegacyValidate = async (runtime2, message2) => {
242
- const text = message2.content.text?.toLowerCase() || "";
243
- const keywords = ["reply", "respond", "answer", "comment"];
244
- const hasKeyword = keywords.some((keyword) => text.includes(keyword));
245
- const hasParentCast = !!(message2.content.metadata && message2.content.metadata.parentCastHash);
246
- const service = runtime2.getService(FARCASTER_SERVICE_NAME);
247
- const isServiceAvailable = !!service?.getMessageService(runtime2.agentId);
248
- return hasKeyword && (hasParentCast || isServiceAvailable);
249
- };
250
- try {
251
- return Boolean(await __avLegacyValidate(runtime, message, state, options));
252
- } catch {
253
- return false;
254
- }
255
- },
256
- handler: async (runtime, message, state) => {
257
- try {
258
- const service = runtime.getService(FARCASTER_SERVICE_NAME);
259
- const messageService = service?.getMessageService(runtime.agentId);
260
- if (!messageService) {
261
- runtime.logger.error("[REPLY_TO_CAST] MessageService not available");
262
- return { success: false, error: "MessageService not available" };
263
- }
264
- const metadata = message.content.metadata;
265
- const stateParentCastHash = typeof state?.parentCastHash === "string" ? state.parentCastHash : undefined;
266
- const parentCastHash = metadata?.parentCastHash ?? stateParentCastHash;
267
- if (!parentCastHash) {
268
- runtime.logger.error("[REPLY_TO_CAST] No parent cast to reply to");
269
- return { success: false, error: "No parent cast to reply to" };
270
- }
271
- let replyContent = "";
272
- if (state?.replyContent) {
273
- replyContent = state.replyContent;
274
- } else {
275
- const prompt = `Based on this request: "${message.content.text}", generate a helpful and engaging reply for a Farcaster cast (max 320 characters).`;
276
- const response = await runtime.useModel(import_core.ModelType.TEXT_LARGE, {
277
- prompt
278
- });
279
- replyContent = typeof response === "string" ? response : String(response);
280
- }
281
- if (replyContent.length > 320) {
282
- replyContent = `${replyContent.substring(0, 317)}...`;
283
- }
284
- const reply = await messageService.sendMessage({
285
- agentId: runtime.agentId,
286
- roomId: message.roomId,
287
- text: replyContent,
288
- type: "REPLY" /* REPLY */,
289
- replyToId: parentCastHash,
290
- metadata: {
291
- parentHash: parentCastHash
292
- }
293
- });
294
- runtime.logger.info(`[REPLY_TO_CAST] Successfully replied to cast: ${reply.id}`);
295
- return { success: true, text: `Replied to cast: ${reply.id}` };
296
- } catch (error) {
297
- runtime.logger.error("[REPLY_TO_CAST] Error replying to cast:", typeof error === "string" ? error : error.message);
298
- throw error;
299
- }
300
- }
301
- };
302
-
303
- // actions/sendCast.ts
304
- var import_core2 = require("@elizaos/core");
305
- var spec2 = requireActionSpec("SEND_CAST");
306
- var sendCastAction = {
307
- name: spec2.name,
308
- similes: spec2.similes ? [...spec2.similes] : [],
309
- description: spec2.description,
310
- examples: spec2.examples ?? [],
311
- validate: async (runtime, message, state, options) => {
312
- const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
313
- const __avText = __avTextRaw.toLowerCase();
314
- const __avKeywords = ["send", "cast"];
315
- const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
316
- const __avRegex = /\b(?:send|cast)\b/i;
317
- const __avRegexOk = Boolean(__avText.match(__avRegex));
318
- const __avSource = String(message?.content?.source ?? message?.source ?? "");
319
- const __avExpectedSource = "farcaster";
320
- const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
321
- const __avOptions = options && typeof options === "object" ? options : {};
322
- const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
323
- if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
324
- return false;
325
- }
326
- const __avLegacyValidate = async (runtime2, message2) => {
327
- const text = message2.content.text?.toLowerCase() || "";
328
- const keywords = ["post", "cast", "share", "announce", "farcaster"];
329
- const hasKeyword = keywords.some((keyword) => text.includes(keyword));
330
- const service = runtime2.getService(FARCASTER_SERVICE_NAME);
331
- const isServiceAvailable = !!service?.getCastService(runtime2.agentId);
332
- return hasKeyword && isServiceAvailable;
333
- };
334
- try {
335
- return Boolean(await __avLegacyValidate(runtime, message, state, options));
336
- } catch {
337
- return false;
338
- }
339
- },
340
- handler: async (runtime, message, state) => {
341
- try {
342
- const service = runtime.getService(FARCASTER_SERVICE_NAME);
343
- const postService = service?.getCastService(runtime.agentId);
344
- if (!postService) {
345
- runtime.logger.error("[SEND_CAST] PostService not available");
346
- return { success: false, error: "PostService not available" };
347
- }
348
- let castContent = "";
349
- if (state?.castContent) {
350
- castContent = state.castContent;
351
- } else {
352
- const prompt = `Based on this request: "${message.content.text}", generate a concise Farcaster cast (max 320 characters). Be engaging and use appropriate hashtags if relevant.`;
353
- const response = await runtime.useModel(import_core2.ModelType.TEXT_LARGE, {
354
- prompt
355
- });
356
- castContent = typeof response === "string" ? response : String(response);
357
- }
358
- if (castContent.length > 320) {
359
- castContent = `${castContent.substring(0, 317)}...`;
360
- }
361
- const cast = await postService.createCast({
362
- agentId: runtime.agentId,
363
- roomId: import_core2.createUniqueUuid(runtime, "farcaster-timeline"),
364
- text: castContent
365
- });
366
- runtime.logger.info(`[SEND_CAST] Successfully posted cast: ${cast.id}`);
367
- await runtime.createMemory({
368
- agentId: runtime.agentId,
369
- roomId: cast.roomId,
370
- entityId: runtime.agentId,
371
- content: {
372
- text: castContent,
373
- source: "farcaster",
374
- metadata: {
375
- castHash: String(cast.metadata?.castHash || ""),
376
- action: "SEND_CAST"
377
- }
378
- },
379
- createdAt: cast.timestamp
380
- }, "messages");
381
- return { success: true, text: `Posted cast: ${cast.id}` };
382
- } catch (error) {
383
- runtime.logger.error("[SEND_CAST] Error posting cast:", typeof error === "string" ? error : error.message);
384
- return { success: false, error: error.message };
385
- }
386
- }
387
- };
388
-
389
- // actions/index.ts
390
- var farcasterActions = [sendCastAction, replyCastAction];
391
-
392
119
  // utils/config.ts
393
- var import_core3 = require("@elizaos/core");
394
- var import_zod2 = require("zod");
120
+ var DEFAULT_FARCASTER_ACCOUNT_ID = "default";
395
121
  function getProcessEnv() {
396
122
  if (typeof process === "undefined") {
397
123
  return {};
@@ -405,41 +131,138 @@ function safeParseInt(value, defaultValue) {
405
131
  const parsed = Number.parseInt(value, 10);
406
132
  return Number.isNaN(parsed) ? defaultValue : Math.max(1, parsed);
407
133
  }
408
- function getFarcasterFid(runtime) {
409
- const fidStr = runtime.getSetting("FARCASTER_FID");
134
+ function stringSetting(runtime, key) {
135
+ const value = runtime.getSetting(key);
136
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
137
+ }
138
+ function characterConfig(runtime) {
139
+ const settings = runtime.character?.settings;
140
+ const raw = settings?.farcaster;
141
+ return raw && typeof raw === "object" ? raw : {};
142
+ }
143
+ function parseAccountsJson(runtime) {
144
+ const raw = stringSetting(runtime, "FARCASTER_ACCOUNTS");
145
+ if (!raw)
146
+ return {};
147
+ try {
148
+ const parsed = JSON.parse(raw);
149
+ if (Array.isArray(parsed)) {
150
+ return Object.fromEntries(parsed.filter((item) => Boolean(item) && typeof item === "object").map((item) => [normalizeFarcasterAccountId(item.accountId ?? item.id), item]));
151
+ }
152
+ return parsed && typeof parsed === "object" ? parsed : {};
153
+ } catch {
154
+ return {};
155
+ }
156
+ }
157
+ function allAccountConfigs(runtime) {
158
+ return {
159
+ ...characterConfig(runtime).accounts ?? {},
160
+ ...parseAccountsJson(runtime)
161
+ };
162
+ }
163
+ function accountConfig(runtime, accountId) {
164
+ const accounts = allAccountConfigs(runtime);
165
+ return accounts[accountId] ?? accounts[normalizeFarcasterAccountId(accountId)] ?? {};
166
+ }
167
+ function rawField(record, keys) {
168
+ if (!record)
169
+ return;
170
+ for (const key of keys) {
171
+ const value = record[key];
172
+ if (typeof value === "string" && value.trim())
173
+ return value.trim();
174
+ if (typeof value === "number" || typeof value === "boolean")
175
+ return String(value);
176
+ }
177
+ return;
178
+ }
179
+ function normalizeFarcasterAccountId(accountId) {
180
+ if (typeof accountId !== "string")
181
+ return DEFAULT_FARCASTER_ACCOUNT_ID;
182
+ const trimmed = accountId.trim();
183
+ return trimmed || DEFAULT_FARCASTER_ACCOUNT_ID;
184
+ }
185
+ function listFarcasterAccountIds(runtime) {
186
+ const ids = new Set;
187
+ const config = characterConfig(runtime);
188
+ if (stringSetting(runtime, "FARCASTER_FID") || config.FARCASTER_FID && config.FARCASTER_SIGNER_UUID) {
189
+ ids.add(DEFAULT_FARCASTER_ACCOUNT_ID);
190
+ }
191
+ for (const id of Object.keys(allAccountConfigs(runtime))) {
192
+ ids.add(normalizeFarcasterAccountId(id));
193
+ }
194
+ return Array.from(ids.size ? ids : new Set([DEFAULT_FARCASTER_ACCOUNT_ID])).sort((a, b) => a.localeCompare(b));
195
+ }
196
+ function resolveDefaultFarcasterAccountId(runtime) {
197
+ const requested = stringSetting(runtime, "FARCASTER_DEFAULT_ACCOUNT_ID") ?? stringSetting(runtime, "FARCASTER_ACCOUNT_ID");
198
+ if (requested)
199
+ return normalizeFarcasterAccountId(requested);
200
+ const ids = listFarcasterAccountIds(runtime);
201
+ return ids.includes(DEFAULT_FARCASTER_ACCOUNT_ID) ? DEFAULT_FARCASTER_ACCOUNT_ID : ids[0];
202
+ }
203
+ function readFarcasterAccountId(...sources) {
204
+ for (const source of sources) {
205
+ if (!source || typeof source !== "object")
206
+ continue;
207
+ const record = source;
208
+ const parameters = record.parameters && typeof record.parameters === "object" ? record.parameters : {};
209
+ const data = record.data && typeof record.data === "object" ? record.data : {};
210
+ const metadata = record.metadata && typeof record.metadata === "object" ? record.metadata : {};
211
+ const farcaster = data.farcaster && typeof data.farcaster === "object" ? data.farcaster : {};
212
+ const value = record.accountId ?? parameters.accountId ?? data.accountId ?? farcaster.accountId ?? metadata.accountId;
213
+ if (typeof value === "string" && value.trim())
214
+ return normalizeFarcasterAccountId(value);
215
+ }
216
+ return;
217
+ }
218
+ function getFarcasterFid(runtime, accountId) {
219
+ const normalizedAccountId = normalizeFarcasterAccountId(accountId ?? resolveDefaultFarcasterAccountId(runtime));
220
+ const account = accountConfig(runtime, normalizedAccountId);
221
+ const base = characterConfig(runtime);
222
+ const allowEnv = normalizedAccountId === DEFAULT_FARCASTER_ACCOUNT_ID;
223
+ const fidStr = rawField(account, ["FARCASTER_FID", "fid"]) ?? rawField(base, ["FARCASTER_FID", "fid"]) ?? (allowEnv ? stringSetting(runtime, "FARCASTER_FID") : undefined);
410
224
  if (!fidStr)
411
225
  return null;
412
226
  const fid = Number.parseInt(fidStr, 10);
413
227
  return Number.isNaN(fid) ? null : fid;
414
228
  }
415
- function hasFarcasterEnabled(runtime) {
416
- const fid = runtime.getSetting("FARCASTER_FID");
417
- const signerUuid = runtime.getSetting("FARCASTER_SIGNER_UUID");
418
- const apiKey = runtime.getSetting("FARCASTER_NEYNAR_API_KEY");
229
+ function hasFarcasterEnabled(runtime, accountId) {
230
+ const normalizedAccountId = normalizeFarcasterAccountId(accountId ?? resolveDefaultFarcasterAccountId(runtime));
231
+ const account = accountConfig(runtime, normalizedAccountId);
232
+ const base = characterConfig(runtime);
233
+ const allowEnv = normalizedAccountId === DEFAULT_FARCASTER_ACCOUNT_ID;
234
+ const fid = getFarcasterFid(runtime, normalizedAccountId);
235
+ const signerUuid = rawField(account, ["FARCASTER_SIGNER_UUID", "signerUuid"]) ?? rawField(base, ["FARCASTER_SIGNER_UUID", "signerUuid"]) ?? (allowEnv ? stringSetting(runtime, "FARCASTER_SIGNER_UUID") : undefined);
236
+ const apiKey = rawField(account, ["FARCASTER_NEYNAR_API_KEY", "neynarApiKey", "apiKey"]) ?? rawField(base, ["FARCASTER_NEYNAR_API_KEY", "neynarApiKey", "apiKey"]) ?? (allowEnv ? stringSetting(runtime, "FARCASTER_NEYNAR_API_KEY") : undefined);
419
237
  runtime.logger.debug(`[hasFarcasterEnabled] FID: ${fid ? "Found" : "Missing"}`);
420
238
  runtime.logger.debug(`[hasFarcasterEnabled] Signer UUID: ${signerUuid ? "Found" : "Missing"}`);
421
239
  runtime.logger.debug(`[hasFarcasterEnabled] API Key: ${apiKey ? "Found" : "Missing"}`);
422
240
  return !!(fid && signerUuid && apiKey);
423
241
  }
424
- function validateFarcasterConfig(runtime) {
425
- const fid = getFarcasterFid(runtime);
242
+ function validateFarcasterConfig(runtime, accountId) {
243
+ const normalizedAccountId = normalizeFarcasterAccountId(accountId ?? resolveDefaultFarcasterAccountId(runtime));
244
+ const account = accountConfig(runtime, normalizedAccountId);
245
+ const base = characterConfig(runtime);
246
+ const allowEnv = normalizedAccountId === DEFAULT_FARCASTER_ACCOUNT_ID;
247
+ const field = (keys, envKey) => rawField(account, keys) ?? rawField(base, keys) ?? (allowEnv && envKey ? stringSetting(runtime, envKey) : undefined);
248
+ const fid = getFarcasterFid(runtime, normalizedAccountId);
426
249
  try {
427
250
  const farcasterConfig = {
428
- FARCASTER_DRY_RUN: runtime.getSetting("FARCASTER_DRY_RUN") || import_core3.parseBooleanFromText(env.FARCASTER_DRY_RUN || "false"),
251
+ FARCASTER_DRY_RUN: field(["FARCASTER_DRY_RUN", "dryRun"], "FARCASTER_DRY_RUN") || import_core.parseBooleanFromText(env.FARCASTER_DRY_RUN || "false"),
429
252
  FARCASTER_FID: fid ?? undefined,
430
- MAX_CAST_LENGTH: safeParseInt(runtime.getSetting("MAX_CAST_LENGTH"), DEFAULT_MAX_CAST_LENGTH),
431
- FARCASTER_POLL_INTERVAL: safeParseInt(runtime.getSetting("FARCASTER_POLL_INTERVAL"), DEFAULT_POLL_INTERVAL),
432
- ENABLE_CAST: runtime.getSetting("ENABLE_CAST") || import_core3.parseBooleanFromText(env.ENABLE_CAST || "true"),
433
- CAST_INTERVAL_MIN: safeParseInt(runtime.getSetting("CAST_INTERVAL_MIN"), DEFAULT_CAST_INTERVAL_MIN),
434
- CAST_INTERVAL_MAX: safeParseInt(runtime.getSetting("CAST_INTERVAL_MAX"), DEFAULT_CAST_INTERVAL_MAX),
435
- ENABLE_ACTION_PROCESSING: runtime.getSetting("ENABLE_ACTION_PROCESSING") || import_core3.parseBooleanFromText(env.ENABLE_ACTION_PROCESSING || "false"),
436
- ACTION_INTERVAL: safeParseInt(runtime.getSetting("ACTION_INTERVAL"), 5),
437
- CAST_IMMEDIATELY: runtime.getSetting("CAST_IMMEDIATELY") || import_core3.parseBooleanFromText(env.CAST_IMMEDIATELY || "false"),
438
- MAX_ACTIONS_PROCESSING: safeParseInt(runtime.getSetting("MAX_ACTIONS_PROCESSING"), 1),
439
- FARCASTER_SIGNER_UUID: runtime.getSetting("FARCASTER_SIGNER_UUID"),
440
- FARCASTER_NEYNAR_API_KEY: runtime.getSetting("FARCASTER_NEYNAR_API_KEY"),
441
- FARCASTER_HUB_URL: runtime.getSetting("FARCASTER_HUB_URL") || "hub.pinata.cloud",
442
- FARCASTER_MODE: runtime.getSetting("FARCASTER_MODE") || "polling"
253
+ MAX_CAST_LENGTH: safeParseInt(field(["MAX_CAST_LENGTH", "maxCastLength"], "MAX_CAST_LENGTH"), DEFAULT_MAX_CAST_LENGTH),
254
+ FARCASTER_POLL_INTERVAL: safeParseInt(field(["FARCASTER_POLL_INTERVAL", "pollInterval"], "FARCASTER_POLL_INTERVAL"), DEFAULT_POLL_INTERVAL),
255
+ ENABLE_CAST: field(["ENABLE_CAST", "enableCast"], "ENABLE_CAST") || import_core.parseBooleanFromText(env.ENABLE_CAST || "true"),
256
+ CAST_INTERVAL_MIN: safeParseInt(field(["CAST_INTERVAL_MIN", "castIntervalMin"], "CAST_INTERVAL_MIN"), DEFAULT_CAST_INTERVAL_MIN),
257
+ CAST_INTERVAL_MAX: safeParseInt(field(["CAST_INTERVAL_MAX", "castIntervalMax"], "CAST_INTERVAL_MAX"), DEFAULT_CAST_INTERVAL_MAX),
258
+ ENABLE_ACTION_PROCESSING: field(["ENABLE_ACTION_PROCESSING", "enableActionProcessing"], "ENABLE_ACTION_PROCESSING") || import_core.parseBooleanFromText(env.ENABLE_ACTION_PROCESSING || "false"),
259
+ ACTION_INTERVAL: safeParseInt(field(["ACTION_INTERVAL", "actionInterval"], "ACTION_INTERVAL"), 5),
260
+ CAST_IMMEDIATELY: field(["CAST_IMMEDIATELY", "castImmediately"], "CAST_IMMEDIATELY") || import_core.parseBooleanFromText(env.CAST_IMMEDIATELY || "false"),
261
+ MAX_ACTIONS_PROCESSING: safeParseInt(field(["MAX_ACTIONS_PROCESSING", "maxActionsProcessing"], "MAX_ACTIONS_PROCESSING"), 1),
262
+ FARCASTER_SIGNER_UUID: field(["FARCASTER_SIGNER_UUID", "signerUuid"], "FARCASTER_SIGNER_UUID"),
263
+ FARCASTER_NEYNAR_API_KEY: field(["FARCASTER_NEYNAR_API_KEY", "neynarApiKey", "apiKey"], "FARCASTER_NEYNAR_API_KEY"),
264
+ FARCASTER_HUB_URL: field(["FARCASTER_HUB_URL", "hubUrl"], "FARCASTER_HUB_URL") || "hub.pinata.cloud",
265
+ FARCASTER_MODE: field(["FARCASTER_MODE", "mode"], "FARCASTER_MODE") || "polling"
443
266
  };
444
267
  runtime.logger.debug(`[validateFarcasterConfig] Resolved FID: ${farcasterConfig.FARCASTER_FID}`);
445
268
  runtime.logger.debug(`[validateFarcasterConfig] Resolved Signer UUID: ${farcasterConfig.FARCASTER_SIGNER_UUID ? "Found" : "Missing"}`);
@@ -459,9 +282,9 @@ function validateFarcasterConfig(runtime) {
459
282
  if (isDryRun) {
460
283
  runtime.logger.info("Farcaster client initialized in dry run mode - no actual casts should be posted");
461
284
  }
462
- return config;
285
+ return { ...config, accountId: normalizedAccountId };
463
286
  } catch (error) {
464
- if (error instanceof import_zod2.z.ZodError) {
287
+ if (error instanceof import_zod.z.ZodError) {
465
288
  const errorMessages = error.issues.map((err) => `${err.path.join(".")}: ${err.message}`).join(`
466
289
  `);
467
290
  throw new Error(`Farcaster configuration validation failed:
@@ -471,32 +294,143 @@ ${errorMessages}`);
471
294
  }
472
295
  }
473
296
 
297
+ // connector-account-provider.ts
298
+ var FARCASTER_PROVIDER_ID = "farcaster";
299
+ function toConnectorAccount(runtime, accountId) {
300
+ let connected = false;
301
+ let fid;
302
+ let hubUrl = "";
303
+ try {
304
+ const config = validateFarcasterConfig(runtime, accountId);
305
+ fid = config.FARCASTER_FID;
306
+ hubUrl = config.FARCASTER_HUB_URL ?? "";
307
+ connected = Boolean(fid && config.FARCASTER_SIGNER_UUID && config.FARCASTER_NEYNAR_API_KEY);
308
+ } catch {
309
+ connected = false;
310
+ }
311
+ const now = Date.now();
312
+ return {
313
+ id: accountId,
314
+ provider: FARCASTER_PROVIDER_ID,
315
+ label: fid ? `FID ${fid}` : accountId,
316
+ role: "OWNER",
317
+ purpose: ["posting", "reading"],
318
+ accessGate: "open",
319
+ status: connected ? "connected" : "disabled",
320
+ externalId: fid ? String(fid) : undefined,
321
+ displayHandle: fid ? String(fid) : undefined,
322
+ createdAt: now,
323
+ updatedAt: now,
324
+ metadata: {
325
+ hubUrl,
326
+ fid
327
+ }
328
+ };
329
+ }
330
+ function createFarcasterConnectorAccountProvider(runtime) {
331
+ return {
332
+ provider: FARCASTER_PROVIDER_ID,
333
+ label: "Farcaster",
334
+ listAccounts: async (_manager) => {
335
+ const ids = listFarcasterAccountIds(runtime);
336
+ return ids.map((id) => toConnectorAccount(runtime, id));
337
+ },
338
+ createAccount: async (input, _manager) => {
339
+ return {
340
+ ...input,
341
+ provider: FARCASTER_PROVIDER_ID,
342
+ role: input.role ?? "OWNER",
343
+ purpose: input.purpose ?? ["posting", "reading"],
344
+ accessGate: input.accessGate ?? "open",
345
+ status: input.status ?? "pending"
346
+ };
347
+ },
348
+ patchAccount: async (_accountId, patch, _manager) => {
349
+ return { ...patch, provider: FARCASTER_PROVIDER_ID };
350
+ },
351
+ deleteAccount: async (_accountId, _manager) => {}
352
+ };
353
+ }
354
+
355
+ // generated/specs/specs.ts
356
+ var coreActionsSpec = {
357
+ version: "1.0.0",
358
+ actions: []
359
+ };
360
+ var allActionsSpec = {
361
+ version: "1.0.0",
362
+ actions: []
363
+ };
364
+ var coreProvidersSpec = {
365
+ version: "1.0.0",
366
+ providers: [
367
+ {
368
+ name: "farcasterProfile",
369
+ description: "Provides information about the agent",
370
+ dynamic: true
371
+ }
372
+ ]
373
+ };
374
+ var allProvidersSpec = {
375
+ version: "1.0.0",
376
+ providers: [
377
+ {
378
+ name: "farcasterProfile",
379
+ description: "Provides information about the agent",
380
+ dynamic: true
381
+ }
382
+ ]
383
+ };
384
+ var coreActionDocs = coreActionsSpec.actions;
385
+ var allActionDocs = allActionsSpec.actions;
386
+ var coreProviderDocs = coreProvidersSpec.providers;
387
+ var allProviderDocs = allProvidersSpec.providers;
388
+
389
+ // generated/specs/spec-helpers.ts
390
+ var coreActionMap = new Map(coreActionDocs.map((doc) => [doc.name, doc]));
391
+ var allActionMap = new Map(allActionDocs.map((doc) => [doc.name, doc]));
392
+ var coreProviderMap = new Map(coreProviderDocs.map((doc) => [doc.name, doc]));
393
+ var allProviderMap = new Map(allProviderDocs.map((doc) => [doc.name, doc]));
394
+ function getProviderSpec(name) {
395
+ return coreProviderMap.get(name) ?? allProviderMap.get(name);
396
+ }
397
+ function requireProviderSpec(name) {
398
+ const spec = getProviderSpec(name);
399
+ if (!spec) {
400
+ throw new Error(`Provider spec not found: ${name}`);
401
+ }
402
+ return spec;
403
+ }
404
+
474
405
  // providers/profileProvider.ts
475
- var spec3 = requireProviderSpec("farcasterProfile");
406
+ var spec = requireProviderSpec("farcasterProfile");
407
+ var MAX_PROFILE_FIELD_LENGTH = 280;
408
+ function truncateProfileField(value) {
409
+ return value ? value.slice(0, MAX_PROFILE_FIELD_LENGTH) : value;
410
+ }
476
411
  var farcasterProfileProvider = {
477
- name: spec3.name,
412
+ name: spec.name,
478
413
  description: "Provides information about the agent's Farcaster profile",
414
+ descriptionCompressed: "provide information agent Farcaster profile",
479
415
  dynamic: true,
480
- get: async (runtime, _message, _state) => {
416
+ contexts: ["social_posting", "messaging", "connectors"],
417
+ contextGate: { anyOf: ["social_posting", "messaging", "connectors"] },
418
+ cacheStable: false,
419
+ cacheScope: "turn",
420
+ get: async (runtime, message, state) => {
481
421
  try {
482
422
  const service = runtime.getService(FARCASTER_SERVICE_NAME);
483
- const managers = service?.getActiveManagers();
484
- if (!managers || managers.size === 0) {
423
+ const accountId = readFarcasterAccountId(message, state);
424
+ const manager = service?.getManagerForAccount?.(accountId, runtime.agentId) ?? service?.getManagerForAccount?.(undefined, runtime.agentId);
425
+ if (!manager) {
485
426
  runtime.logger.debug("[FarcasterProfileProvider] No managers available");
486
427
  return {
487
428
  text: "Farcaster profile not available.",
488
429
  data: { available: false }
489
430
  };
490
431
  }
491
- const manager = managers.get(runtime.agentId);
492
- if (!manager) {
493
- runtime.logger.debug("[FarcasterProfileProvider] No manager for this agent");
494
- return {
495
- text: "Farcaster profile not available for this agent.",
496
- data: { available: false }
497
- };
498
- }
499
- const fid = getFarcasterFid(runtime);
432
+ const selectedAccountId = manager.config.accountId;
433
+ const fid = getFarcasterFid(runtime, selectedAccountId);
500
434
  if (!fid) {
501
435
  runtime.logger.warn("[FarcasterProfileProvider] Invalid or missing FARCASTER_FID");
502
436
  return {
@@ -506,18 +440,21 @@ var farcasterProfileProvider = {
506
440
  }
507
441
  try {
508
442
  const profile = await manager.client.getProfile(fid);
443
+ const username = truncateProfileField(profile.username) ?? "";
444
+ const name = truncateProfileField(profile.name);
509
445
  return {
510
- text: `Your Farcaster profile: @${profile.username} (FID: ${profile.fid}). ${profile.name ? `Display name: ${profile.name}` : ""}`,
446
+ text: `Your Farcaster profile: @${username} (FID: ${profile.fid}). ${name ? `Display name: ${name}` : ""}`,
511
447
  data: {
512
448
  available: true,
513
449
  fid: profile.fid,
514
- username: profile.username,
515
- name: profile.name,
516
- pfp: profile.pfp
450
+ username,
451
+ name,
452
+ pfp: profile.pfp,
453
+ accountId: selectedAccountId
517
454
  },
518
455
  values: {
519
456
  fid: profile.fid,
520
- username: profile.username
457
+ username
521
458
  }
522
459
  };
523
460
  } catch (error) {
@@ -537,165 +474,38 @@ var farcasterProfileProvider = {
537
474
  }
538
475
  };
539
476
 
540
- // providers/threadProvider.ts
541
- var formatTimestamp = (timestamp) => {
542
- return new Date(timestamp).toLocaleString("en-US", {
543
- hour: "2-digit",
544
- minute: "2-digit",
545
- month: "short",
546
- day: "numeric"
547
- });
548
- };
549
- var spec4 = requireProviderSpec("farcasterThread");
550
- var farcasterThreadProvider = {
551
- name: spec4.name,
552
- description: "Provides thread context for Farcaster casts so the agent can reference the full conversation.",
553
- dynamic: true,
554
- get: async (runtime, message, _state) => {
555
- const contentForSource = message.content;
556
- const source = typeof contentForSource.source === "string" ? contentForSource.source : undefined;
557
- if (source !== FARCASTER_SOURCE) {
558
- return {
559
- text: "",
560
- data: {
561
- available: false,
562
- reason: `Not a Farcaster message (source=${source ?? "unknown"})`
563
- }
564
- };
565
- }
566
- const service = runtime.getService(FARCASTER_SERVICE_NAME);
567
- const messageService = service?.getMessageService(runtime.agentId);
568
- if (!messageService) {
569
- return {
570
- text: "Farcaster message service not available.",
571
- data: { available: false, error: "service_unavailable" }
572
- };
573
- }
574
- const content = message.content;
575
- const messageMetadata = message.metadata ?? {};
576
- const contentMetadata = typeof content.metadata === "object" && content.metadata !== null && !Array.isArray(content.metadata) ? content.metadata : undefined;
577
- const castHash = content.hash || content.castHash || contentMetadata?.castHash || messageMetadata.castHash || contentMetadata?.parentHash || messageMetadata.parentHash;
578
- if (!castHash || typeof castHash !== "string") {
579
- return {
580
- text: "Unable to resolve Farcaster cast hash for this message.",
581
- data: { available: false, error: "missing_cast_hash" }
582
- };
583
- }
584
- const threadMessages = await messageService.getThread({
585
- agentId: runtime.agentId,
586
- castHash
587
- });
588
- if (!threadMessages || threadMessages.length === 0) {
589
- runtime.logger.debug({ castHash }, "[FarcasterThreadProvider] No thread messages retrieved for cast.");
590
- return {
591
- text: "No Farcaster thread context available.",
592
- data: { available: false, castHash, count: 0 }
593
- };
594
- }
595
- const formattedThread = threadMessages.map((msg, index) => {
596
- const time = formatTimestamp(msg.timestamp);
597
- const username = msg.username || msg.userId || "unknown";
598
- const marker = index === threadMessages.length - 1 ? "→" : "•";
599
- const text = msg.text && msg.text.trim().length > 0 ? msg.text : "<no text>";
600
- return `${marker} [${time}] @${username}: ${text}`;
601
- }).join(`
602
- `);
603
- return {
604
- text: `# Farcaster Thread Context
605
- ${formattedThread}`,
606
- data: {
607
- available: true,
608
- castHash,
609
- count: threadMessages.length
610
- },
611
- values: {
612
- farcasterThread: formattedThread,
613
- farcasterCastHash: castHash,
614
- farcasterCurrentCastText: threadMessages[threadMessages.length - 1]?.text ?? "",
615
- farcasterParentCastText: threadMessages.length > 1 ? threadMessages[threadMessages.length - 2]?.text ?? "" : ""
616
- }
617
- };
618
- }
619
- };
620
-
621
- // providers/timelineProvider.ts
622
- function getTimeAgo(date) {
623
- const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
624
- if (seconds < 60)
625
- return "just now";
626
- if (seconds < 3600)
627
- return `${Math.floor(seconds / 60)}m ago`;
628
- if (seconds < 86400)
629
- return `${Math.floor(seconds / 3600)}h ago`;
630
- return `${Math.floor(seconds / 86400)}d ago`;
631
- }
632
- var spec5 = requireProviderSpec("farcasterTimeline");
633
- var farcasterTimelineProvider = {
634
- name: spec5.name,
635
- description: "Provides recent casts from the agent's Farcaster timeline",
636
- dynamic: true,
637
- get: async (runtime, _message, _state) => {
638
- try {
639
- const service = runtime.getService(FARCASTER_SERVICE_NAME);
640
- const castService = service?.getCastService(runtime.agentId);
641
- if (!castService) {
642
- return {
643
- text: "Farcaster timeline not available.",
644
- data: { available: false }
645
- };
646
- }
647
- const casts = await castService.getCasts({
648
- agentId: runtime.agentId,
649
- limit: 5
650
- });
651
- if (!casts || casts.length === 0) {
652
- return {
653
- text: "No recent casts in your timeline.",
654
- data: {
655
- available: true,
656
- casts: [],
657
- count: 0
658
- }
659
- };
660
- }
661
- const formattedCasts = casts.map((cast, index) => {
662
- const timeAgo = getTimeAgo(new Date(cast.timestamp));
663
- return `${index + 1}. @${cast.username} (${timeAgo}): ${cast.text}`;
664
- }).join(`
665
- `);
666
- return {
667
- text: `Recent casts from your timeline:
668
- ${formattedCasts}`,
669
- data: {
670
- available: true,
671
- castCount: casts.length
672
- },
673
- values: {
674
- latestCastHash: String(casts[0]?.metadata?.castHash || ""),
675
- latestCastText: casts[0]?.text || ""
676
- }
677
- };
678
- } catch (error) {
679
- runtime.logger.error("[FarcasterTimelineProvider] Error:", typeof error === "string" ? error : error.message);
680
- return {
681
- text: "Unable to fetch Farcaster timeline.",
682
- data: {
683
- available: false,
684
- error: error instanceof Error ? error.message : "Unknown error"
685
- }
686
- };
687
- }
688
- }
689
- };
690
-
691
477
  // providers/index.ts
692
- var farcasterProviders = [
693
- farcasterProfileProvider,
694
- farcasterTimelineProvider,
695
- farcasterThreadProvider
696
- ];
478
+ var farcasterProviders = [farcasterProfileProvider];
697
479
 
698
480
  // routes/webhook.ts
481
+ function isRecord(value) {
482
+ return typeof value === "object" && value !== null;
483
+ }
484
+ function isNeynarWebhookData(value) {
485
+ if (!isRecord(value) || typeof value.type !== "string")
486
+ return false;
487
+ if (value.data === undefined)
488
+ return true;
489
+ if (!isRecord(value.data))
490
+ return false;
491
+ if (typeof value.data.hash !== "string")
492
+ return false;
493
+ if (!isRecord(value.data.author) || typeof value.data.author.fid !== "number")
494
+ return false;
495
+ if (value.data.text !== undefined && typeof value.data.text !== "string") {
496
+ return false;
497
+ }
498
+ if (value.data.mentioned_profiles !== undefined && !(Array.isArray(value.data.mentioned_profiles) && value.data.mentioned_profiles.every((profile) => isRecord(profile) && typeof profile.fid === "number"))) {
499
+ return false;
500
+ }
501
+ if (value.data.parent_hash !== undefined && typeof value.data.parent_hash !== "string") {
502
+ return false;
503
+ }
504
+ if (value.data.parent_author !== undefined && !(isRecord(value.data.parent_author) && typeof value.data.parent_author.fid === "number")) {
505
+ return false;
506
+ }
507
+ return true;
508
+ }
699
509
  var farcasterWebhookRoutes = [
700
510
  {
701
511
  type: "POST",
@@ -703,16 +513,25 @@ var farcasterWebhookRoutes = [
703
513
  path: "/webhook",
704
514
  handler: async (req, res, runtime) => {
705
515
  try {
516
+ if (!isNeynarWebhookData(req.body)) {
517
+ res.status(400).json({
518
+ success: false,
519
+ error: "Invalid webhook payload"
520
+ });
521
+ return;
522
+ }
706
523
  const webhookData = req.body;
707
524
  const eventType = webhookData.type;
708
- const farcasterService = runtime?.getService?.("farcaster");
709
- if (farcasterService) {
710
- const agentManager = farcasterService.managers?.get?.(runtime.agentId ?? "");
711
- if (agentManager?.interactions) {
712
- if (agentManager.interactions.mode === "webhook") {
713
- await agentManager.interactions.processWebhookData?.(webhookData);
714
- }
525
+ const farcasterService = runtime?.getService?.(FARCASTER_SERVICE_NAME);
526
+ const accountId = readFarcasterAccountId(webhookData);
527
+ if (farcasterService && accountId) {
528
+ const manager = farcasterService.getManagerForAccount?.(accountId, runtime.agentId);
529
+ if (manager?.interactions.mode === "webhook") {
530
+ await manager.interactions.processWebhookData(webhookData);
715
531
  }
532
+ } else if (farcasterService) {
533
+ const managers = farcasterService.getManagersForAgent?.(runtime.agentId) ?? new Map;
534
+ await Promise.all(Array.from(managers.values()).filter((manager) => manager.interactions.mode === "webhook").map((manager) => manager.interactions.processWebhookData(webhookData)));
716
535
  }
717
536
  res.status(200).json({
718
537
  success: true,
@@ -734,24 +553,24 @@ var farcasterWebhookRoutes = [
734
553
  ];
735
554
 
736
555
  // services/FarcasterService.ts
737
- var import_core11 = require("@elizaos/core");
556
+ var import_core9 = require("@elizaos/core");
738
557
 
739
558
  // managers/AgentManager.ts
740
559
  var import_nodejs_sdk2 = require("@neynar/nodejs-sdk");
741
560
 
742
561
  // client/FarcasterClient.ts
743
- var import_core5 = require("@elizaos/core");
562
+ var import_core3 = require("@elizaos/core");
744
563
  var import_nodejs_sdk = require("@neynar/nodejs-sdk");
745
564
  var import_lru_cache = require("lru-cache");
746
565
 
747
566
  // utils/index.ts
748
- var import_core4 = require("@elizaos/core");
567
+ var import_core2 = require("@elizaos/core");
749
568
  var MAX_CAST_LENGTH = 1024;
750
569
  function castId({ hash, agentId }) {
751
570
  return `${hash}-${agentId}`;
752
571
  }
753
572
  function castUuid(props) {
754
- return import_core4.stringToUuid(castId(props));
573
+ return import_core2.stringToUuid(castId(props));
755
574
  }
756
575
  function splitPostContent(content, maxLength = MAX_CAST_LENGTH) {
757
576
  const paragraphs = content.split(`
@@ -860,7 +679,8 @@ function createCastMemory({
860
679
  roomId,
861
680
  senderId,
862
681
  runtime,
863
- cast
682
+ cast,
683
+ accountId
864
684
  }) {
865
685
  const inReplyTo = cast.inReplyTo ? castUuid({
866
686
  hash: cast.inReplyTo.hash,
@@ -876,17 +696,45 @@ function createCastMemory({
876
696
  content: {
877
697
  text: cast.text,
878
698
  source: FARCASTER_SOURCE,
699
+ ...accountId ? { accountId } : {},
879
700
  url: "",
880
701
  inReplyTo,
881
702
  hash: cast.hash,
882
703
  threadId: cast.threadId,
883
704
  attachments: cast.media && cast.media.length > 0 ? cast.media : undefined
884
705
  },
706
+ metadata: {
707
+ ...accountId ? { accountId } : {}
708
+ },
885
709
  roomId
886
710
  };
887
711
  }
888
712
 
889
713
  // client/FarcasterClient.ts
714
+ async function logNeynarCall(op, context, fn) {
715
+ const startedAt = Date.now();
716
+ import_core3.elizaLogger.debug({ sdk: "neynar", op, ...context }, `[FarcasterClient] ${op} started`);
717
+ try {
718
+ const result = await fn();
719
+ import_core3.elizaLogger.info({
720
+ sdk: "neynar",
721
+ op,
722
+ ...context,
723
+ durationMs: Date.now() - startedAt
724
+ }, `[FarcasterClient] ${op} ok`);
725
+ return result;
726
+ } catch (error) {
727
+ const message = error instanceof Error ? error.message : String(error);
728
+ import_core3.elizaLogger.warn({
729
+ sdk: "neynar",
730
+ op,
731
+ ...context,
732
+ durationMs: Date.now() - startedAt,
733
+ error: message
734
+ }, `[FarcasterClient] ${op} failed`);
735
+ throw error;
736
+ }
737
+ }
890
738
  var castCache = new import_lru_cache.LRUCache({
891
739
  max: DEFAULT_CAST_CACHE_SIZE,
892
740
  ttl: DEFAULT_CAST_CACHE_TTL
@@ -921,21 +769,20 @@ class FarcasterClient {
921
769
  }
922
770
  async publishCast(cast, parentCastId) {
923
771
  try {
924
- const result = await this.neynar.publishCast({
772
+ const result = await logNeynarCall("publishCast", { textLen: cast.length, parentHash: parentCastId?.hash ?? null }, async () => this.neynar.publishCast({
925
773
  signerUuid: this.signerUuid,
926
774
  text: cast,
927
775
  parent: parentCastId?.hash
928
- });
776
+ }));
929
777
  if (result.success) {
930
778
  return this.getCast(result.cast.hash);
931
779
  }
932
780
  throw new Error(`[Farcaster] Error publishing [${cast}] parentCastId: [${parentCastId}]`);
933
781
  } catch (err) {
934
782
  if (import_nodejs_sdk.isApiErrorResponse(err)) {
935
- import_core5.elizaLogger.error(`Neynar error: ${JSON.stringify(err.response.data)}`);
783
+ import_core3.elizaLogger.error(`Neynar error: ${JSON.stringify(err.response.data)}`);
936
784
  throw err.response.data;
937
785
  } else {
938
- import_core5.elizaLogger.error(`Error: ${JSON.stringify(err)}`);
939
786
  throw err;
940
787
  }
941
788
  }
@@ -945,19 +792,19 @@ class FarcasterClient {
945
792
  if (cachedCast) {
946
793
  return cachedCast;
947
794
  }
948
- const response = await this.neynar.lookupCastByHashOrUrl({
795
+ const response = await logNeynarCall("lookupCastByHashOrUrl", { castHash }, async () => this.neynar.lookupCastByHashOrUrl({
949
796
  identifier: castHash,
950
797
  type: "hash"
951
- });
798
+ }));
952
799
  castCache.set(castHash, response.cast);
953
800
  return response.cast;
954
801
  }
955
802
  async getMentions(request) {
956
- const neynarMentionsResponse = await this.neynar.fetchAllNotifications({
803
+ const neynarMentionsResponse = await logNeynarCall("fetchAllNotifications", { fid: request.fid, pageSize: request.pageSize }, async () => this.neynar.fetchAllNotifications({
957
804
  fid: request.fid,
958
805
  type: ["mentions", "replies"],
959
806
  limit: request.pageSize
960
- });
807
+ }));
961
808
  const mentions = [];
962
809
  for (const notification of neynarMentionsResponse.notifications) {
963
810
  const neynarCast = notification.cast;
@@ -971,35 +818,29 @@ class FarcasterClient {
971
818
  if (profileCache.has(fid)) {
972
819
  return profileCache.get(fid);
973
820
  }
974
- try {
975
- const result = await this.neynar.fetchBulkUsers({ fids: [fid] });
976
- if (!result.users || result.users.length < 1) {
977
- import_core5.elizaLogger.error("Error fetching user by fid");
978
- throw new Error("Profile fetch failed");
979
- }
980
- const neynarUserProfile = result.users[0];
981
- const profile = {
982
- fid,
983
- name: "",
984
- username: ""
985
- };
986
- profile.name = neynarUserProfile.display_name ?? "";
987
- profile.username = neynarUserProfile.username;
988
- profile.bio = neynarUserProfile.profile.bio.text;
989
- profile.pfp = neynarUserProfile.pfp_url;
990
- profileCache.set(fid, profile);
991
- return profile;
992
- } catch (error) {
993
- import_core5.elizaLogger.error(`Error fetching profile: ${JSON.stringify(error)}`);
994
- throw error;
821
+ const result = await logNeynarCall("fetchBulkUsers", { fid }, async () => this.neynar.fetchBulkUsers({ fids: [fid] }));
822
+ if (result.users.length < 1) {
823
+ throw new Error("Profile fetch failed");
995
824
  }
825
+ const neynarUserProfile = result.users[0];
826
+ const profile = {
827
+ fid,
828
+ name: "",
829
+ username: ""
830
+ };
831
+ profile.name = neynarUserProfile.display_name ?? "";
832
+ profile.username = neynarUserProfile.username;
833
+ profile.bio = neynarUserProfile.profile.bio.text;
834
+ profile.pfp = neynarUserProfile.pfp_url;
835
+ profileCache.set(fid, profile);
836
+ return profile;
996
837
  }
997
838
  async getTimeline(request) {
998
839
  const timeline = [];
999
- const response = await this.neynar.fetchCastsForUser({
840
+ const response = await logNeynarCall("fetchCastsForUser", { fid: request.fid, pageSize: request.pageSize }, async () => this.neynar.fetchCastsForUser({
1000
841
  fid: request.fid,
1001
842
  limit: request.pageSize
1002
- });
843
+ }));
1003
844
  for (const cast of response.casts) {
1004
845
  castCache.set(cast.hash, cast);
1005
846
  timeline.push(neynarCastToCast(cast));
@@ -1016,42 +857,40 @@ class FarcasterClient {
1016
857
  }
1017
858
  async publishReaction(params) {
1018
859
  try {
1019
- const result = await this.neynar.publishReaction({
860
+ const result = await logNeynarCall("publishReaction", { reactionType: params.reactionType, target: params.target }, async () => this.neynar.publishReaction({
1020
861
  signerUuid: this.signerUuid,
1021
862
  reactionType: params.reactionType,
1022
863
  target: params.target
1023
- });
864
+ }));
1024
865
  return { success: result.success ?? false };
1025
866
  } catch (err) {
1026
867
  if (import_nodejs_sdk.isApiErrorResponse(err)) {
1027
- import_core5.elizaLogger.error(`Neynar error publishing reaction: ${JSON.stringify(err.response.data)}`);
868
+ import_core3.elizaLogger.error(`Neynar error publishing reaction: ${JSON.stringify(err.response.data)}`);
1028
869
  throw err.response.data;
1029
870
  }
1030
- import_core5.elizaLogger.error(`Error publishing reaction: ${JSON.stringify(err)}`);
1031
871
  throw err;
1032
872
  }
1033
873
  }
1034
874
  async deleteReaction(params) {
1035
875
  try {
1036
- const result = await this.neynar.deleteReaction({
876
+ const result = await logNeynarCall("deleteReaction", { reactionType: params.reactionType, target: params.target }, async () => this.neynar.deleteReaction({
1037
877
  signerUuid: this.signerUuid,
1038
878
  reactionType: params.reactionType,
1039
879
  target: params.target
1040
- });
880
+ }));
1041
881
  return { success: result.success ?? false };
1042
882
  } catch (err) {
1043
883
  if (import_nodejs_sdk.isApiErrorResponse(err)) {
1044
- import_core5.elizaLogger.error(`Neynar error deleting reaction: ${JSON.stringify(err.response.data)}`);
884
+ import_core3.elizaLogger.error(`Neynar error deleting reaction: ${JSON.stringify(err.response.data)}`);
1045
885
  throw err.response.data;
1046
886
  }
1047
- import_core5.elizaLogger.error(`Error deleting reaction: ${JSON.stringify(err)}`);
1048
887
  throw err;
1049
888
  }
1050
889
  }
1051
890
  }
1052
891
 
1053
892
  // managers/CastManager.ts
1054
- var import_core6 = require("@elizaos/core");
893
+ var import_core4 = require("@elizaos/core");
1055
894
 
1056
895
  // utils/callbacks.ts
1057
896
  function standardCastHandlerCallback({
@@ -1065,6 +904,7 @@ function standardCastHandlerCallback({
1065
904
  }) {
1066
905
  const callback = async (content) => {
1067
906
  try {
907
+ const accountId = normalizeFarcasterAccountId(config.accountId ?? DEFAULT_FARCASTER_ACCOUNT_ID);
1068
908
  if (config.FARCASTER_DRY_RUN) {
1069
909
  runtime.logger.info(`[Farcaster] Dry run: would have cast: ${content.text}`);
1070
910
  return [];
@@ -1082,7 +922,8 @@ function standardCastHandlerCallback({
1082
922
  roomId,
1083
923
  senderId: runtime.agentId,
1084
924
  runtime,
1085
- cast: neynarCastToCast(cast)
925
+ cast: neynarCastToCast(cast),
926
+ accountId
1086
927
  });
1087
928
  if (i === 0) {
1088
929
  memory.content.actions = content.actions;
@@ -1119,6 +960,9 @@ class FarcasterCastManager {
1119
960
  this.config = opts.config;
1120
961
  this.fid = this.config.FARCASTER_FID;
1121
962
  }
963
+ getAccountId() {
964
+ return normalizeFarcasterAccountId(this.config.accountId ?? DEFAULT_FARCASTER_ACCOUNT_ID);
965
+ }
1122
966
  async start() {
1123
967
  if (this.isRunning || !this.config.ENABLE_CAST) {
1124
968
  return;
@@ -1161,43 +1005,56 @@ class FarcasterCastManager {
1161
1005
  }
1162
1006
  }
1163
1007
  async generateNewCast() {
1164
- this.runtime.logger.info("Generating new cast");
1165
- try {
1166
- const worldId = import_core6.createUniqueUuid(this.runtime, this.fid.toString());
1167
- const roomId = import_core6.createUniqueUuid(this.runtime, `${this.fid}-home`);
1168
- const callback = standardCastHandlerCallback({
1169
- client: this.client,
1170
- runtime: this.runtime,
1171
- config: this.config,
1172
- roomId,
1173
- onCompletion: async (casts, _memories) => {
1174
- const lastCast = casts[casts.length - 1];
1175
- await this.runtime.setCache(lastCastCacheKey(this.fid), {
1176
- hash: lastCast.hash,
1177
- timestamp: new Date(lastCast.timestamp).getTime()
1178
- });
1179
- }
1180
- });
1181
- await this.runtime.emitEvent(import_core6.EventType.POST_GENERATED, {
1182
- runtime: this.runtime,
1183
- callback,
1184
- worldId,
1185
- userId: this.runtime.agentId,
1186
- roomId,
1187
- source: FARCASTER_SOURCE
1188
- });
1189
- await this.runtime.emitEvent("FARCASTER_CAST_GENERATED" /* CAST_GENERATED */, {
1190
- runtime: this.runtime,
1191
- source: FARCASTER_SOURCE
1192
- });
1193
- } catch (error) {
1194
- this.runtime.logger.error({ error }, "[Farcaster] Error generating new cast");
1195
- }
1008
+ await import_core4.withStandaloneTrajectory(this.runtime, {
1009
+ source: "plugin-farcaster:auto-cast",
1010
+ metadata: {
1011
+ platform: FARCASTER_SOURCE,
1012
+ kind: "public_post_generation",
1013
+ fid: this.fid,
1014
+ accountId: this.getAccountId()
1015
+ }
1016
+ }, async () => {
1017
+ import_core4.setTrajectoryPurpose("background");
1018
+ this.runtime.logger.info("Generating new cast");
1019
+ try {
1020
+ const worldId = import_core4.createUniqueUuid(this.runtime, this.fid.toString());
1021
+ const roomId = import_core4.createUniqueUuid(this.runtime, `${this.fid}-home`);
1022
+ const callback = standardCastHandlerCallback({
1023
+ client: this.client,
1024
+ runtime: this.runtime,
1025
+ config: this.config,
1026
+ roomId,
1027
+ onCompletion: async (casts, _memories) => {
1028
+ const lastCast = casts[casts.length - 1];
1029
+ await this.runtime.setCache(lastCastCacheKey(this.fid), {
1030
+ hash: lastCast.hash,
1031
+ timestamp: new Date(lastCast.timestamp).getTime()
1032
+ });
1033
+ }
1034
+ });
1035
+ await this.runtime.emitEvent(import_core4.EventType.POST_GENERATED, {
1036
+ runtime: this.runtime,
1037
+ callback,
1038
+ worldId,
1039
+ userId: this.runtime.agentId,
1040
+ roomId,
1041
+ source: FARCASTER_SOURCE,
1042
+ accountId: this.getAccountId()
1043
+ });
1044
+ await this.runtime.emitEvent("FARCASTER_POST_GENERATED" /* POST_GENERATED */, {
1045
+ runtime: this.runtime,
1046
+ source: FARCASTER_SOURCE,
1047
+ accountId: this.getAccountId()
1048
+ });
1049
+ } catch (error) {
1050
+ this.runtime.logger.error({ error }, "[Farcaster] Error generating new cast");
1051
+ }
1052
+ });
1196
1053
  }
1197
1054
  }
1198
1055
 
1199
1056
  // managers/InteractionManager.ts
1200
- var import_core8 = require("@elizaos/core");
1057
+ var import_core6 = require("@elizaos/core");
1201
1058
 
1202
1059
  // utils/asyncqueue.ts
1203
1060
  class AsyncQueue {
@@ -1261,12 +1118,12 @@ class AsyncQueue {
1261
1118
  }
1262
1119
 
1263
1120
  // managers/EmbedManager.ts
1264
- var import_core7 = require("@elizaos/core");
1121
+ var import_core5 = require("@elizaos/core");
1265
1122
  function isEmbedUrl(embed) {
1266
- return "url" in embed && typeof embed.url === "string";
1123
+ return "url" in embed;
1267
1124
  }
1268
1125
  function isEmbedCast(embed) {
1269
- return "cast" in embed && typeof embed.cast === "object";
1126
+ return "cast" in embed;
1270
1127
  }
1271
1128
  function getMediaTypeFromUrl(url, contentType) {
1272
1129
  const lowerUrl = url.toLowerCase();
@@ -1360,10 +1217,10 @@ class EmbedManager {
1360
1217
  let description = "An image attachment";
1361
1218
  let title = "Image";
1362
1219
  try {
1363
- const result = await this.runtime.useModel(import_core7.ModelType.IMAGE_DESCRIPTION, {
1220
+ const result = await import_core5.withStandaloneTrajectory(this.runtime, { source: "farcaster-embed" }, async () => this.runtime.useModel(import_core5.ModelType.IMAGE_DESCRIPTION, {
1364
1221
  prompt: "Analyze this image and provide a concise title and description. Focus on the main subject and any notable details.",
1365
1222
  imageUrl: url
1366
- });
1223
+ }));
1367
1224
  if (result && typeof result === "object") {
1368
1225
  const typedResult = result;
1369
1226
  description = typedResult.description || description;
@@ -1650,6 +1507,9 @@ class FarcasterInteractionManager {
1650
1507
  });
1651
1508
  this.runtime.logger.info(`Farcaster interaction mode: ${this.mode}`);
1652
1509
  }
1510
+ getAccountId() {
1511
+ return normalizeFarcasterAccountId(this.config.accountId ?? DEFAULT_FARCASTER_ACCOUNT_ID);
1512
+ }
1653
1513
  async processMention(cast) {
1654
1514
  const agentFid = this.config.FARCASTER_FID;
1655
1515
  const agent = await this.client.getProfile(agentFid);
@@ -1744,9 +1604,9 @@ class FarcasterInteractionManager {
1744
1604
  hash: cast.hash
1745
1605
  });
1746
1606
  const conversationId = cast.threadId ?? cast.inReplyTo?.hash ?? cast.hash;
1747
- const entityId = import_core8.createUniqueUuid(this.runtime, cast.authorFid.toString());
1748
- const worldId = import_core8.createUniqueUuid(this.runtime, cast.authorFid.toString());
1749
- const roomId = import_core8.createUniqueUuid(this.runtime, conversationId);
1607
+ const entityId = import_core6.createUniqueUuid(this.runtime, cast.authorFid.toString());
1608
+ const worldId = import_core6.createUniqueUuid(this.runtime, cast.authorFid.toString());
1609
+ const roomId = import_core6.createUniqueUuid(this.runtime, conversationId);
1750
1610
  if (entityId !== this.runtime.agentId) {
1751
1611
  await this.runtime.ensureConnection({
1752
1612
  entityId,
@@ -1755,13 +1615,15 @@ class FarcasterInteractionManager {
1755
1615
  userName: cast.profile.username,
1756
1616
  name: cast.profile.name,
1757
1617
  source: FARCASTER_SOURCE,
1758
- type: import_core8.ChannelType.THREAD,
1618
+ type: import_core6.ChannelType.THREAD,
1759
1619
  channelId: conversationId,
1760
- messageServerId: import_core8.stringToUuid(cast.authorFid.toString()),
1620
+ messageServerId: import_core6.stringToUuid(cast.authorFid.toString()),
1761
1621
  worldId,
1762
1622
  metadata: {
1623
+ accountId: this.getAccountId(),
1763
1624
  ownership: { ownerId: cast.authorFid.toString() },
1764
1625
  farcaster: {
1626
+ accountId: this.getAccountId(),
1765
1627
  username: cast.profile.username,
1766
1628
  id: cast.authorFid.toString(),
1767
1629
  name: cast.profile.name
@@ -1786,12 +1648,16 @@ class FarcasterInteractionManager {
1786
1648
  hash: cast.inReplyTo.hash
1787
1649
  }) : undefined,
1788
1650
  source: FARCASTER_SOURCE,
1789
- channelType: import_core8.ChannelType.THREAD,
1651
+ accountId: this.getAccountId(),
1652
+ channelType: import_core6.ChannelType.THREAD,
1790
1653
  attachments: cast.media && cast.media.length > 0 ? cast.media : undefined
1791
1654
  },
1792
1655
  entityId,
1793
1656
  roomId,
1794
- createdAt: cast.timestamp.getTime()
1657
+ createdAt: cast.timestamp.getTime(),
1658
+ metadata: {
1659
+ accountId: this.getAccountId()
1660
+ }
1795
1661
  };
1796
1662
  return memory;
1797
1663
  });
@@ -1820,7 +1686,8 @@ class FarcasterInteractionManager {
1820
1686
  runtime,
1821
1687
  memory: newMemory,
1822
1688
  cast: currentCast,
1823
- source: FARCASTER_SOURCE
1689
+ source: FARCASTER_SOURCE,
1690
+ accountId: self.getAccountId()
1824
1691
  });
1825
1692
  }
1826
1693
  thread.unshift(currentCast);
@@ -1874,6 +1741,7 @@ class FarcasterInteractionManager {
1874
1741
  memory,
1875
1742
  cast,
1876
1743
  source: FARCASTER_SOURCE,
1744
+ accountId: this.getAccountId(),
1877
1745
  callback
1878
1746
  };
1879
1747
  this.runtime.emitEvent("FARCASTER_MENTION_RECEIVED" /* MENTION_RECEIVED */, mentionPayload);
@@ -1894,8 +1762,10 @@ class FarcasterAgentManager {
1894
1762
  client;
1895
1763
  casts;
1896
1764
  interactions;
1765
+ config;
1897
1766
  constructor(runtime, config) {
1898
1767
  this.runtime = runtime;
1768
+ this.config = config;
1899
1769
  const signerUuid = config.FARCASTER_SIGNER_UUID;
1900
1770
  const neynarConfig = new import_nodejs_sdk2.Configuration({
1901
1771
  apiKey: config.FARCASTER_NEYNAR_API_KEY
@@ -1920,18 +1790,40 @@ class FarcasterAgentManager {
1920
1790
  }
1921
1791
 
1922
1792
  // services/CastService.ts
1923
- var import_core9 = require("@elizaos/core");
1793
+ var import_core7 = require("@elizaos/core");
1794
+ function clampLimit(value, fallback, max) {
1795
+ if (!Number.isFinite(value)) {
1796
+ return fallback;
1797
+ }
1798
+ return Math.min(Math.max(1, Math.floor(value)), max);
1799
+ }
1800
+ function readContentString(content, keys) {
1801
+ const record = content;
1802
+ for (const key of keys) {
1803
+ const value = record[key];
1804
+ if (typeof value === "string" && value.trim()) {
1805
+ return value.trim();
1806
+ }
1807
+ }
1808
+ return;
1809
+ }
1810
+
1924
1811
  class FarcasterCastService {
1925
1812
  client;
1926
1813
  runtime;
1814
+ accountId;
1927
1815
  static serviceType = "ICastService";
1928
- constructor(client, runtime) {
1816
+ constructor(client, runtime, accountId = DEFAULT_FARCASTER_ACCOUNT_ID) {
1929
1817
  this.client = client;
1930
1818
  this.runtime = runtime;
1819
+ this.accountId = accountId;
1820
+ }
1821
+ getAccountId() {
1822
+ return normalizeFarcasterAccountId(this.accountId);
1931
1823
  }
1932
1824
  async getCasts(params) {
1933
1825
  try {
1934
- const fid = getFarcasterFid(this.runtime);
1826
+ const fid = getFarcasterFid(this.runtime, this.getAccountId());
1935
1827
  if (!fid) {
1936
1828
  this.runtime.logger.error("FARCASTER_FID is not configured");
1937
1829
  return [];
@@ -1974,6 +1866,7 @@ class FarcasterCastService {
1974
1866
  inReplyTo: params.replyTo?.hash,
1975
1867
  media: [],
1976
1868
  metadata: {
1869
+ accountId: this.getAccountId(),
1977
1870
  castHash: cast.hash,
1978
1871
  authorFid: cast.authorFid,
1979
1872
  source: FARCASTER_SOURCE,
@@ -1987,6 +1880,58 @@ class FarcasterCastService {
1987
1880
  throw error;
1988
1881
  }
1989
1882
  }
1883
+ async handleSendPost(runtime, content) {
1884
+ const requestedAccountId = normalizeFarcasterAccountId(readFarcasterAccountId(content) ?? this.getAccountId());
1885
+ if (requestedAccountId !== this.getAccountId()) {
1886
+ throw new Error(`Farcaster account '${requestedAccountId}' is not available in this service instance`);
1887
+ }
1888
+ const text = typeof content.text === "string" ? content.text.trim() : "";
1889
+ if (!text) {
1890
+ throw new Error("Farcaster post connector requires non-empty text content.");
1891
+ }
1892
+ const parentHash = readContentString(content, ["parentHash", "replyTo", "replyToHash"]);
1893
+ const fid = getFarcasterFid(this.runtime, this.getAccountId());
1894
+ const cast = await this.createCast({
1895
+ agentId: runtime.agentId,
1896
+ roomId: import_core7.createUniqueUuid(runtime, `farcaster:feed:${fid ?? runtime.agentId}`),
1897
+ text,
1898
+ ...parentHash && fid ? { replyTo: { hash: parentHash, fid } } : {}
1899
+ });
1900
+ return this.farcasterCastToMemory(runtime, cast);
1901
+ }
1902
+ async fetchFeed(context, params = {}) {
1903
+ const requestedAccountId = normalizeFarcasterAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
1904
+ if (requestedAccountId !== this.getAccountId()) {
1905
+ throw new Error(`Farcaster account '${requestedAccountId}' is not available in this service instance`);
1906
+ }
1907
+ const casts = await this.getCasts({
1908
+ agentId: context.runtime.agentId,
1909
+ limit: clampLimit(params.limit, 25, 100),
1910
+ cursor: params.cursor
1911
+ });
1912
+ return casts.map((cast) => this.farcasterCastToMemory(context.runtime, cast));
1913
+ }
1914
+ async searchPosts(context, params) {
1915
+ const requestedAccountId = normalizeFarcasterAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
1916
+ if (requestedAccountId !== this.getAccountId()) {
1917
+ throw new Error(`Farcaster account '${requestedAccountId}' is not available in this service instance`);
1918
+ }
1919
+ const query = params.query.trim().toLowerCase();
1920
+ if (!query) {
1921
+ return [];
1922
+ }
1923
+ const limit = clampLimit(params.limit, 25, 100);
1924
+ const casts = await this.getCasts({
1925
+ agentId: context.runtime.agentId,
1926
+ limit: 100,
1927
+ cursor: params.cursor
1928
+ });
1929
+ return casts.filter((cast) => {
1930
+ const text = cast.text.toLowerCase();
1931
+ const username = cast.username.toLowerCase();
1932
+ return text.includes(query) || username.includes(query);
1933
+ }).slice(0, limit).map((cast) => this.farcasterCastToMemory(context.runtime, cast));
1934
+ }
1990
1935
  async deleteCast(params) {
1991
1936
  this.runtime.logger.warn(`Cast deletion is not supported by the Farcaster API: ${JSON.stringify({ castHash: params.castHash })}`);
1992
1937
  }
@@ -2052,7 +1997,7 @@ class FarcasterCastService {
2052
1997
  }
2053
1998
  async getMentions(params) {
2054
1999
  try {
2055
- const fid = getFarcasterFid(this.runtime);
2000
+ const fid = getFarcasterFid(this.runtime, this.getAccountId());
2056
2001
  if (!fid) {
2057
2002
  this.runtime.logger.error("FARCASTER_FID is not configured");
2058
2003
  return [];
@@ -2073,7 +2018,7 @@ class FarcasterCastService {
2073
2018
  async generateCastContent() {
2074
2019
  const prompt = `Generate an interesting and engaging Farcaster cast. It should be conversational, authentic, and under 320 characters. Topics can include technology, AI, crypto, decentralized social media, or general observations about life.`;
2075
2020
  try {
2076
- const response = await this.runtime.useModel(import_core9.ModelType.TEXT_SMALL, {
2021
+ const response = await this.runtime.useModel(import_core7.ModelType.TEXT_SMALL, {
2077
2022
  prompt,
2078
2023
  maxTokens: 100
2079
2024
  });
@@ -2086,7 +2031,7 @@ class FarcasterCastService {
2086
2031
  async truncateCast(text) {
2087
2032
  const prompt = `Shorten this text to under 320 characters while keeping the main message intact: "${text}"`;
2088
2033
  try {
2089
- const response = await this.runtime.useModel(import_core9.ModelType.TEXT_SMALL, {
2034
+ const response = await this.runtime.useModel(import_core7.ModelType.TEXT_SMALL, {
2090
2035
  prompt,
2091
2036
  maxTokens: 100
2092
2037
  });
@@ -2102,9 +2047,9 @@ class FarcasterCastService {
2102
2047
  }
2103
2048
  async storeCastInMemory(roomId, cast) {
2104
2049
  try {
2105
- const entityId = import_core9.createUniqueUuid(this.runtime, cast.userId);
2050
+ const entityId = import_core7.createUniqueUuid(this.runtime, cast.userId);
2106
2051
  const memory = {
2107
- id: import_core9.createUniqueUuid(this.runtime, cast.id),
2052
+ id: import_core7.createUniqueUuid(this.runtime, cast.id),
2108
2053
  agentId: this.runtime.agentId,
2109
2054
  entityId,
2110
2055
  content: {
@@ -2112,7 +2057,11 @@ class FarcasterCastService {
2112
2057
  castHash: String(cast.metadata?.castHash || ""),
2113
2058
  castId: cast.id,
2114
2059
  author: cast.username,
2115
- timestamp: cast.timestamp
2060
+ timestamp: cast.timestamp,
2061
+ accountId: this.getAccountId()
2062
+ },
2063
+ metadata: {
2064
+ accountId: this.getAccountId()
2116
2065
  },
2117
2066
  roomId,
2118
2067
  createdAt: Date.now()
@@ -2122,11 +2071,58 @@ class FarcasterCastService {
2122
2071
  this.runtime.logger.error(`Failed to store cast in memory: ${JSON.stringify({ error })}`);
2123
2072
  }
2124
2073
  }
2074
+ farcasterCastToMemory(runtime, cast) {
2075
+ const authorId = cast.userId || "unknown";
2076
+ const entityId = authorId === runtime.agentId ? runtime.agentId : import_core7.createUniqueUuid(runtime, `farcaster:user:${authorId}`);
2077
+ const roomId = cast.roomId || import_core7.createUniqueUuid(runtime, `farcaster:feed:${authorId}`);
2078
+ const castHash = typeof cast.metadata?.castHash === "string" ? cast.metadata.castHash : cast.id;
2079
+ return {
2080
+ id: import_core7.createUniqueUuid(runtime, `farcaster:cast:${castHash}`),
2081
+ agentId: runtime.agentId,
2082
+ entityId,
2083
+ roomId,
2084
+ createdAt: cast.timestamp || Date.now(),
2085
+ content: {
2086
+ text: cast.text,
2087
+ source: FARCASTER_SOURCE,
2088
+ channelType: import_core7.ChannelType.FEED,
2089
+ ...cast.inReplyTo ? { inReplyTo: import_core7.createUniqueUuid(runtime, `farcaster:cast:${cast.inReplyTo}`) } : {}
2090
+ },
2091
+ metadata: {
2092
+ type: "message",
2093
+ source: FARCASTER_SOURCE,
2094
+ accountId: this.getAccountId(),
2095
+ provider: FARCASTER_SOURCE,
2096
+ timestamp: cast.timestamp,
2097
+ fromBot: entityId === runtime.agentId,
2098
+ messageIdFull: castHash,
2099
+ chatType: import_core7.ChannelType.FEED,
2100
+ sender: {
2101
+ id: authorId,
2102
+ username: cast.username
2103
+ },
2104
+ farcaster: {
2105
+ accountId: this.getAccountId(),
2106
+ castId: cast.id,
2107
+ castHash,
2108
+ authorFid: cast.metadata?.authorFid,
2109
+ username: cast.username,
2110
+ inReplyTo: cast.inReplyTo,
2111
+ metrics: {
2112
+ recasts: cast.metadata?.recasts,
2113
+ replies: cast.metadata?.replies,
2114
+ likes: cast.metadata?.likes
2115
+ },
2116
+ ...cast.metadata ?? {}
2117
+ }
2118
+ }
2119
+ };
2120
+ }
2125
2121
  castToFarcasterCast(cast, agentId) {
2126
2122
  return {
2127
2123
  id: castUuid({ hash: cast.hash, agentId }),
2128
2124
  agentId,
2129
- roomId: import_core9.createUniqueUuid(this.runtime, cast.threadId || cast.hash),
2125
+ roomId: import_core7.createUniqueUuid(this.runtime, cast.threadId || cast.hash),
2130
2126
  userId: cast.profile.fid.toString(),
2131
2127
  username: cast.profile.username,
2132
2128
  text: cast.text,
@@ -2136,6 +2132,7 @@ class FarcasterCastService {
2136
2132
  castHash: cast.hash,
2137
2133
  authorFid: cast.authorFid,
2138
2134
  source: FARCASTER_SOURCE,
2135
+ accountId: this.getAccountId(),
2139
2136
  ...cast.threadId ? { threadId: cast.threadId } : {},
2140
2137
  ...cast.stats ? {
2141
2138
  recasts: cast.stats.recasts,
@@ -2148,19 +2145,24 @@ class FarcasterCastService {
2148
2145
  }
2149
2146
 
2150
2147
  // services/MessageService.ts
2151
- var import_core10 = require("@elizaos/core");
2148
+ var import_core8 = require("@elizaos/core");
2152
2149
  class FarcasterMessageService {
2153
2150
  client;
2154
2151
  runtime;
2155
- constructor(client, runtime) {
2152
+ accountId;
2153
+ constructor(client, runtime, accountId = DEFAULT_FARCASTER_ACCOUNT_ID) {
2156
2154
  this.client = client;
2157
2155
  this.runtime = runtime;
2156
+ this.accountId = accountId;
2157
+ }
2158
+ getAccountId() {
2159
+ return normalizeFarcasterAccountId(this.accountId);
2158
2160
  }
2159
2161
  castToMessage(cast, agentId, extraMetadata) {
2160
2162
  return {
2161
2163
  id: castUuid({ hash: cast.hash, agentId }),
2162
2164
  agentId,
2163
- roomId: import_core10.createUniqueUuid(this.runtime, cast.threadId || cast.hash),
2165
+ roomId: import_core8.createUniqueUuid(this.runtime, cast.threadId || cast.hash),
2164
2166
  userId: cast.profile.fid.toString(),
2165
2167
  username: cast.profile.username,
2166
2168
  text: cast.text,
@@ -2169,6 +2171,7 @@ class FarcasterMessageService {
2169
2171
  inReplyTo: cast.inReplyTo ? castUuid({ hash: cast.inReplyTo.hash, agentId }) : undefined,
2170
2172
  metadata: {
2171
2173
  source: FARCASTER_SOURCE,
2174
+ accountId: this.getAccountId(),
2172
2175
  castHash: cast.hash,
2173
2176
  threadId: cast.threadId,
2174
2177
  authorFid: cast.authorFid,
@@ -2179,7 +2182,7 @@ class FarcasterMessageService {
2179
2182
  async getMessages(options) {
2180
2183
  try {
2181
2184
  const { agentId, roomId, limit = 20 } = options;
2182
- const fid = getFarcasterFid(this.runtime);
2185
+ const fid = getFarcasterFid(this.runtime, this.getAccountId());
2183
2186
  if (!fid) {
2184
2187
  this.runtime.logger.error("[Farcaster] FARCASTER_FID is not configured");
2185
2188
  return [];
@@ -2203,11 +2206,15 @@ class FarcasterMessageService {
2203
2206
  }
2204
2207
  async sendMessage(options) {
2205
2208
  try {
2209
+ const requestedAccountId = normalizeFarcasterAccountId(options.accountId ?? readFarcasterAccountId(options.metadata) ?? this.getAccountId());
2210
+ if (requestedAccountId !== this.getAccountId()) {
2211
+ throw new Error(`Farcaster account '${requestedAccountId}' is not available in this service instance`);
2212
+ }
2206
2213
  const { text, type, roomId, replyToId, agentId } = options;
2207
2214
  let inReplyTo;
2208
2215
  if (replyToId && type === "REPLY" /* REPLY */) {
2209
2216
  const parentHash = typeof options.metadata?.parentHash === "string" ? options.metadata.parentHash : replyToId;
2210
- const fid = getFarcasterFid(this.runtime);
2217
+ const fid = getFarcasterFid(this.runtime, this.getAccountId());
2211
2218
  if (!fid) {
2212
2219
  throw new Error("FARCASTER_FID is not configured");
2213
2220
  }
@@ -2227,15 +2234,16 @@ class FarcasterMessageService {
2227
2234
  const message = this.castToMessage(cast, agentId, options.metadata);
2228
2235
  message.roomId = roomId;
2229
2236
  message.type = type;
2230
- const castGeneratedPayload = {
2237
+ const postGeneratedPayload = {
2231
2238
  runtime: this.runtime,
2232
2239
  source: FARCASTER_SOURCE,
2233
2240
  castHash: cast.hash,
2234
2241
  ...cast.threadId ? { threadId: cast.threadId } : {},
2235
2242
  messageId: message.id,
2236
- roomId
2243
+ roomId,
2244
+ accountId: this.getAccountId()
2237
2245
  };
2238
- await this.runtime.emitEvent("FARCASTER_CAST_GENERATED" /* CAST_GENERATED */, castGeneratedPayload);
2246
+ await this.runtime.emitEvent("FARCASTER_POST_GENERATED" /* POST_GENERATED */, postGeneratedPayload);
2239
2247
  return message;
2240
2248
  } catch (error) {
2241
2249
  const err = error instanceof Error ? error : new Error(String(error));
@@ -2285,11 +2293,9 @@ class FarcasterMessageService {
2285
2293
  }
2286
2294
 
2287
2295
  // services/FarcasterService.ts
2288
- class FarcasterService extends import_core11.Service {
2296
+ class FarcasterService extends import_core9.Service {
2289
2297
  static instance;
2290
- managers = new Map;
2291
- messageServices = new Map;
2292
- castServices = new Map;
2298
+ agents = new Map;
2293
2299
  static serviceType = FARCASTER_SERVICE_NAME;
2294
2300
  description = "Farcaster integration service for sending and receiving casts";
2295
2301
  capabilityDescription = "The agent is able to send and receive messages on farcaster";
@@ -2304,8 +2310,7 @@ class FarcasterService extends import_core11.Service {
2304
2310
  }
2305
2311
  static async start(runtime) {
2306
2312
  const service = FarcasterService.getInstance();
2307
- let manager = service.managers.get(runtime.agentId);
2308
- if (manager) {
2313
+ if (service.agents.has(runtime.agentId)) {
2309
2314
  runtime.logger.warn({ agentId: runtime.agentId }, "Farcaster service already started");
2310
2315
  return service;
2311
2316
  }
@@ -2313,80 +2318,213 @@ class FarcasterService extends import_core11.Service {
2313
2318
  runtime.logger.debug({ agentId: runtime.agentId }, "Farcaster service not enabled");
2314
2319
  return service;
2315
2320
  }
2316
- const farcasterConfig = validateFarcasterConfig(runtime);
2317
- manager = new FarcasterAgentManager(runtime, farcasterConfig);
2318
- service.managers.set(runtime.agentId, manager);
2319
- const messageService = new FarcasterMessageService(manager.client, runtime);
2320
- const castService = new FarcasterCastService(manager.client, runtime);
2321
- service.messageServices.set(runtime.agentId, messageService);
2322
- service.castServices.set(runtime.agentId, castService);
2323
- await manager.start();
2324
- runtime.logger.success({ agentId: runtime.agentId }, "Farcaster client started");
2321
+ const accounts = {
2322
+ defaultAccountId: normalizeFarcasterAccountId(resolveDefaultFarcasterAccountId(runtime)),
2323
+ managers: new Map,
2324
+ messageServices: new Map,
2325
+ castServices: new Map
2326
+ };
2327
+ service.agents.set(runtime.agentId, accounts);
2328
+ for (const accountId of listFarcasterAccountIds(runtime)) {
2329
+ if (!hasFarcasterEnabled(runtime, accountId)) {
2330
+ continue;
2331
+ }
2332
+ const farcasterConfig = validateFarcasterConfig(runtime, accountId);
2333
+ const manager = new FarcasterAgentManager(runtime, farcasterConfig);
2334
+ accounts.managers.set(accountId, manager);
2335
+ accounts.messageServices.set(accountId, new FarcasterMessageService(manager.client, runtime, accountId));
2336
+ accounts.castServices.set(accountId, new FarcasterCastService(manager.client, runtime, accountId));
2337
+ await manager.start();
2338
+ runtime.logger.success({ agentId: runtime.agentId, accountId }, "Farcaster client started");
2339
+ }
2325
2340
  return service;
2326
2341
  }
2327
2342
  static async stop(runtime) {
2328
2343
  const service = FarcasterService.getInstance();
2329
- const manager = service.managers.get(runtime.agentId);
2330
- if (manager) {
2331
- await manager.stop();
2332
- service.managers.delete(runtime.agentId);
2333
- service.messageServices.delete(runtime.agentId);
2334
- service.castServices.delete(runtime.agentId);
2344
+ const accounts = service.agents.get(runtime.agentId);
2345
+ if (accounts) {
2346
+ for (const manager of accounts.managers.values()) {
2347
+ await manager.stop();
2348
+ }
2349
+ service.agents.delete(runtime.agentId);
2335
2350
  runtime.logger.info({ agentId: runtime.agentId }, "Farcaster client stopped");
2336
2351
  } else {
2337
2352
  runtime.logger.debug({ agentId: runtime.agentId }, "Farcaster service not running");
2338
2353
  }
2339
2354
  }
2355
+ static registerSendHandlers(runtime, serviceInstance) {
2356
+ const accounts = serviceInstance?.agents.get(runtime.agentId);
2357
+ if (!accounts || accounts.castServices.size === 0) {
2358
+ runtime.logger.warn({ src: "plugin:farcaster", agentId: runtime.agentId }, "Cannot register Farcaster post connector; cast service is not initialized");
2359
+ return;
2360
+ }
2361
+ const withPostConnector = runtime;
2362
+ if (typeof withPostConnector.registerPostConnector !== "function") {
2363
+ return;
2364
+ }
2365
+ for (const castService of accounts.castServices.values()) {
2366
+ const accountId = castService.getAccountId();
2367
+ withPostConnector.registerPostConnector({
2368
+ source: "farcaster",
2369
+ accountId,
2370
+ label: "Farcaster",
2371
+ description: "Farcaster public cast connector for publishing casts and reading or searching the authenticated account's recent feed.",
2372
+ capabilities: ["post", "fetch_feed", "search_posts"],
2373
+ contexts: ["social", "social_posting", "connectors"],
2374
+ metadata: {
2375
+ accountId,
2376
+ service: FARCASTER_SERVICE_NAME
2377
+ },
2378
+ postHandler: castService.handleSendPost.bind(castService),
2379
+ fetchFeed: castService.fetchFeed.bind(castService),
2380
+ searchPosts: castService.searchPosts.bind(castService),
2381
+ contentShaping: {
2382
+ systemPromptFragment: "For Farcaster casts, write a conversational public cast under 320 characters. If replying, keep enough context for a public thread.",
2383
+ constraints: {
2384
+ maxLength: 320,
2385
+ supportsMarkdown: false,
2386
+ channelType: import_core9.ChannelType.FEED
2387
+ }
2388
+ }
2389
+ });
2390
+ }
2391
+ runtime.logger.info({ src: "plugin:farcaster", agentId: runtime.agentId }, "Registered Farcaster post connector");
2392
+ }
2340
2393
  async stop() {
2341
- for (const manager of Array.from(this.managers.values())) {
2342
- const agentId = manager.runtime.agentId;
2343
- manager.runtime.logger.debug("Stopping Farcaster service");
2394
+ for (const [agentId, accounts] of Array.from(this.agents.entries())) {
2395
+ const runtime = accounts.managers.values().next().value?.runtime;
2396
+ runtime?.logger.debug("Stopping Farcaster service");
2344
2397
  try {
2345
- await FarcasterService.stop(manager.runtime);
2398
+ if (runtime) {
2399
+ await FarcasterService.stop(runtime);
2400
+ } else {
2401
+ this.agents.delete(agentId);
2402
+ }
2346
2403
  } catch (error) {
2347
- manager.runtime.logger.error({ agentId, error }, "Error stopping Farcaster service");
2404
+ runtime?.logger.error({ agentId, error }, "Error stopping Farcaster service");
2348
2405
  }
2349
2406
  }
2350
2407
  }
2351
- getMessageService(agentId) {
2352
- return this.messageServices.get(agentId);
2408
+ getMessageService(agentId, accountId) {
2409
+ return this.getMessageServiceForAccount(accountId, agentId);
2410
+ }
2411
+ getCastService(agentId, accountId) {
2412
+ return this.getCastServiceForAccount(accountId, agentId);
2413
+ }
2414
+ getMessageServiceForAccount(accountId, agentId) {
2415
+ const resolvedAgentId = agentId ?? this.firstAgentId();
2416
+ if (!resolvedAgentId)
2417
+ return;
2418
+ const accounts = this.agents.get(resolvedAgentId);
2419
+ if (!accounts)
2420
+ return;
2421
+ const id = accountId ? normalizeFarcasterAccountId(accountId) : accounts.defaultAccountId;
2422
+ return accounts.messageServices.get(id);
2423
+ }
2424
+ getCastServiceForAccount(accountId, agentId) {
2425
+ const resolvedAgentId = agentId ?? this.firstAgentId();
2426
+ if (!resolvedAgentId)
2427
+ return;
2428
+ const accounts = this.agents.get(resolvedAgentId);
2429
+ if (!accounts)
2430
+ return;
2431
+ const id = accountId ? normalizeFarcasterAccountId(accountId) : accounts.defaultAccountId;
2432
+ return accounts.castServices.get(id);
2433
+ }
2434
+ getManagerForAccount(accountId, agentId) {
2435
+ const resolvedAgentId = agentId ?? this.firstAgentId();
2436
+ if (!resolvedAgentId)
2437
+ return;
2438
+ const accounts = this.agents.get(resolvedAgentId);
2439
+ if (!accounts)
2440
+ return;
2441
+ const id = accountId ? normalizeFarcasterAccountId(accountId) : accounts.defaultAccountId;
2442
+ return accounts.managers.get(id);
2353
2443
  }
2354
- getCastService(agentId) {
2355
- return this.castServices.get(agentId);
2444
+ getDefaultAccountId(agentId) {
2445
+ const resolvedAgentId = agentId ?? this.firstAgentId();
2446
+ return resolvedAgentId ? this.agents.get(resolvedAgentId)?.defaultAccountId : undefined;
2447
+ }
2448
+ listAccountIds(agentId) {
2449
+ const resolvedAgentId = agentId ?? this.firstAgentId();
2450
+ const accounts = resolvedAgentId ? this.agents.get(resolvedAgentId) : undefined;
2451
+ return accounts ? Array.from(accounts.managers.keys()) : [];
2452
+ }
2453
+ getManagersForAgent(agentId) {
2454
+ const resolvedAgentId = agentId ?? this.firstAgentId();
2455
+ const accounts = resolvedAgentId ? this.agents.get(resolvedAgentId) : undefined;
2456
+ return new Map(accounts?.managers ?? []);
2356
2457
  }
2357
2458
  async healthCheck() {
2358
2459
  const managerStatuses = {};
2359
2460
  let overallHealthy = true;
2360
- for (const [agentId, manager] of Array.from(this.managers.entries())) {
2361
- try {
2362
- const fid = getFarcasterFid(manager.runtime);
2363
- if (!fid) {
2364
- throw new Error("FARCASTER_FID not configured");
2461
+ for (const [agentId, accounts] of Array.from(this.agents.entries())) {
2462
+ managerStatuses[agentId] = {};
2463
+ for (const [accountId, manager] of Array.from(accounts.managers.entries())) {
2464
+ try {
2465
+ const fid = getFarcasterFid(manager.runtime, accountId);
2466
+ if (!fid) {
2467
+ throw new Error("FARCASTER_FID not configured");
2468
+ }
2469
+ const profile = await manager.client.getProfile(fid);
2470
+ managerStatuses[agentId][accountId] = {
2471
+ status: "healthy",
2472
+ fid: profile.fid,
2473
+ username: profile.username
2474
+ };
2475
+ } catch (error) {
2476
+ managerStatuses[agentId][accountId] = {
2477
+ status: "unhealthy",
2478
+ error: error instanceof Error ? error.message : "Unknown error"
2479
+ };
2480
+ overallHealthy = false;
2365
2481
  }
2366
- const profile = await manager.client.getProfile(fid);
2367
- managerStatuses[agentId] = {
2368
- status: "healthy",
2369
- fid: profile.fid,
2370
- username: profile.username
2371
- };
2372
- } catch (error) {
2373
- managerStatuses[agentId] = {
2374
- status: "unhealthy",
2375
- error: error instanceof Error ? error.message : "Unknown error"
2376
- };
2377
- overallHealthy = false;
2378
2482
  }
2379
2483
  }
2380
2484
  return {
2381
2485
  healthy: overallHealthy,
2382
2486
  details: {
2383
- activeManagers: this.managers.size,
2487
+ activeManagers: Array.from(this.agents.values()).reduce((total, accounts) => total + accounts.managers.size, 0),
2384
2488
  managerStatuses
2385
2489
  }
2386
2490
  };
2387
2491
  }
2388
2492
  getActiveManagers() {
2389
- return new Map(this.managers);
2493
+ return new Map(Array.from(this.agents.entries()).flatMap(([agentId, accounts]) => Array.from(accounts.managers.entries()).map(([accountId, manager]) => [`${agentId}:${accountId}`, manager])));
2494
+ }
2495
+ firstAgentId() {
2496
+ return this.agents.keys().next().value;
2497
+ }
2498
+ }
2499
+
2500
+ // workflow-credential-provider.ts
2501
+ var import_core10 = require("@elizaos/core");
2502
+ var WORKFLOW_CREDENTIAL_PROVIDER_TYPE = "workflow_credential_provider";
2503
+ var SUPPORTED = ["httpHeaderAuth"];
2504
+
2505
+ class FarcasterWorkflowCredentialProvider extends import_core10.Service {
2506
+ static serviceType = WORKFLOW_CREDENTIAL_PROVIDER_TYPE;
2507
+ capabilityDescription = "Supplies Farcaster (Neynar API) credentials to the workflow plugin.";
2508
+ static async start(runtime) {
2509
+ return new FarcasterWorkflowCredentialProvider(runtime);
2510
+ }
2511
+ async stop() {}
2512
+ async resolve(_userId, credType) {
2513
+ if (credType !== "httpHeaderAuth")
2514
+ return null;
2515
+ const neynarApiKey = this.runtime.getSetting("FARCASTER_NEYNAR_API_KEY");
2516
+ if (!neynarApiKey?.trim())
2517
+ return null;
2518
+ return {
2519
+ status: "credential_data",
2520
+ data: { name: "api_key", value: neynarApiKey.trim() }
2521
+ };
2522
+ }
2523
+ checkCredentialTypes(credTypes) {
2524
+ return {
2525
+ supported: credTypes.filter((t) => SUPPORTED.includes(t)),
2526
+ unsupported: credTypes.filter((t) => !SUPPORTED.includes(t))
2527
+ };
2390
2528
  }
2391
2529
  }
2392
2530
 
@@ -2394,11 +2532,24 @@ class FarcasterService extends import_core11.Service {
2394
2532
  var farcasterPlugin = {
2395
2533
  name: "farcaster",
2396
2534
  description: "Farcaster client plugin for sending and receiving casts",
2397
- services: [FarcasterService],
2398
- actions: farcasterActions,
2535
+ services: [FarcasterService, FarcasterWorkflowCredentialProvider],
2536
+ actions: [],
2399
2537
  providers: farcasterProviders,
2400
- routes: farcasterWebhookRoutes
2538
+ routes: farcasterWebhookRoutes,
2539
+ autoEnable: {
2540
+ connectorKeys: ["farcaster"]
2541
+ },
2542
+ async init(_config, runtime) {
2543
+ try {
2544
+ const manager = import_core11.getConnectorAccountManager(runtime);
2545
+ manager.registerProvider(createFarcasterConnectorAccountProvider(runtime));
2546
+ } catch (err) {
2547
+ import_core11.logger.warn({
2548
+ src: "plugin:farcaster",
2549
+ err: err instanceof Error ? err.message : String(err)
2550
+ }, "Failed to register Farcaster provider with ConnectorAccountManager");
2551
+ }
2552
+ }
2401
2553
  };
2402
- var typescript_default = farcasterPlugin;
2403
2554
 
2404
- //# debugId=A19CF1DB7D19D13C64756E2164756E21
2555
+ //# debugId=24E35985E5D4101664756E2164756E21