hybrid 1.3.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +5 -13
  2. package/dist/behaviors/index.cjs +162 -0
  3. package/dist/behaviors/index.cjs.map +1 -0
  4. package/dist/behaviors/index.d.cts +32 -0
  5. package/dist/behaviors/index.d.ts +32 -0
  6. package/dist/behaviors/index.js +133 -0
  7. package/dist/behaviors/index.js.map +1 -0
  8. package/dist/index.cjs +294 -437
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +22 -130
  11. package/dist/index.d.ts +22 -130
  12. package/dist/index.js +289 -430
  13. package/dist/index.js.map +1 -1
  14. package/dist/tools/index.cjs +235 -112
  15. package/dist/tools/index.cjs.map +1 -1
  16. package/dist/tools/index.d.cts +1818 -6
  17. package/dist/tools/index.d.ts +1818 -6
  18. package/dist/tools/index.js +239 -112
  19. package/dist/tools/index.js.map +1 -1
  20. package/package.json +16 -10
  21. package/src/behaviors/filter-messages.test.ts +177 -0
  22. package/src/behaviors/filter-messages.ts +100 -0
  23. package/src/behaviors/index.ts +16 -0
  24. package/src/behaviors/react-with.ts +47 -0
  25. package/src/behaviors/threaded-reply.test.ts +41 -0
  26. package/src/behaviors/threaded-reply.ts +27 -0
  27. package/src/core/agent.ts +28 -107
  28. package/src/core/plugin.ts +7 -36
  29. package/src/core/tool.ts +19 -52
  30. package/src/index.ts +1 -7
  31. package/src/server/listen.ts +52 -112
  32. package/src/server/listen.ts.old +327 -0
  33. package/src/server/{processor.ts → processor.ts.old} +9 -1
  34. package/src/tools/blockchain.ts +11 -10
  35. package/src/tools/xmtp.ts +269 -128
  36. package/README.md.old +0 -167
  37. package/dist/index-C_JSkCHP.d.cts +0 -1927
  38. package/dist/index-C_JSkCHP.d.ts +0 -1927
  39. package/src/types.ts +0 -38
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
- # Hybrid - Typescript Framework for building commerce-connected AI Agents.
1
+ # hybrid
2
2
 
3
- An open-source agent framework for building conversational AI agents on XMTP.
3
+ This package is part of the Hybrid monorepo.
4
4
 
5
5
  Hybrid makes it easy for developers to create intelligent agents that can understand natural language, process messages, and respond through XMTP's decentralized messaging protocol.
6
6
 
7
- See [http://hybriddev.com](http://hybrid.dev) for more information.
7
+ See [hybrid.dev](https://hybrid.dev) for more information.
8
8
 
9
9
  ## 📦 Quickstart
10
10
 
@@ -49,18 +49,10 @@ hybrid register
49
49
 
50
50
  This generates secure wallet and encryption keys for your XMTP agent.
51
51
 
52
- ### 5. Register your wallet with XMTP
53
-
54
- ```bash
55
- hybrid register
56
- ```
57
-
58
- ### 6. Start developing
52
+ ### 5. Start developing
59
53
 
60
54
  ```bash
61
55
  hybrid dev
62
56
  ```
63
57
 
64
- Your agent will start listening for XMTP messages and you're ready to build!
65
-
66
- Go to [https://xmtp.chat/dm/](https://xmtp.chat/dm/) and send a message to your agent.
58
+ Your agent will start listening for XMTP messages and you're ready to build!
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/behaviors/index.ts
21
+ var behaviors_exports = {};
22
+ __export(behaviors_exports, {
23
+ BehaviorRegistryImpl: () => import_types.BehaviorRegistryImpl,
24
+ filter: () => import_xmtp3.filter,
25
+ filterMessages: () => filterMessages,
26
+ reactWith: () => reactWith,
27
+ threadedReply: () => threadedReply
28
+ });
29
+ module.exports = __toCommonJS(behaviors_exports);
30
+ var import_xmtp3 = require("@hybrd/xmtp");
31
+
32
+ // src/behaviors/filter-messages.ts
33
+ var import_utils = require("@hybrd/utils");
34
+ var import_xmtp = require("@hybrd/xmtp");
35
+ function filterMessages(filters) {
36
+ return {
37
+ id: "filter-messages",
38
+ config: {
39
+ enabled: true,
40
+ config: {
41
+ filters: 1
42
+ }
43
+ },
44
+ async before(context) {
45
+ const messageContent = typeof context.message.content === "string" ? context.message.content.substring(0, 100) : String(context.message.content || "unknown");
46
+ import_utils.logger.debug(
47
+ `\u{1F50D} [filter-messages] Processing message: ${messageContent}...`
48
+ );
49
+ const filterAPI = {
50
+ fromSelf: () => import_xmtp.filter.fromSelf(context.message, context.client),
51
+ hasContent: () => import_xmtp.filter.hasContent(context.message),
52
+ isDM: () => import_xmtp.filter.isDM(context.conversation),
53
+ isGroup: () => import_xmtp.filter.isGroup(context.conversation),
54
+ isGroupAdmin: () => import_xmtp.filter.isGroupAdmin(
55
+ context.conversation,
56
+ context.message
57
+ ),
58
+ isGroupSuperAdmin: () => import_xmtp.filter.isGroupSuperAdmin(
59
+ context.conversation,
60
+ context.message
61
+ ),
62
+ isReaction: () => import_xmtp.filter.isReaction(context.message),
63
+ isRemoteAttachment: () => import_xmtp.filter.isRemoteAttachment(context.message),
64
+ isReply: () => import_xmtp.filter.isReply(context.message),
65
+ isText: () => import_xmtp.filter.isText(context.message),
66
+ isTextReply: () => import_xmtp.filter.isTextReply(context.message),
67
+ hasMention: (mention) => {
68
+ const content = typeof context.message.content === "string" ? context.message.content : String(context.message.content || "");
69
+ return content.includes(mention);
70
+ }
71
+ };
72
+ try {
73
+ const passes = filters(filterAPI);
74
+ if (!passes) {
75
+ import_utils.logger.debug(
76
+ `\u{1F507} [filter-messages] Filter failed - message filtered out`
77
+ );
78
+ if (!context.sendOptions) {
79
+ context.sendOptions = {};
80
+ }
81
+ context.sendOptions.filtered = true;
82
+ return;
83
+ }
84
+ import_utils.logger.debug(`\u2705 [filter-messages] Filter passed`);
85
+ } catch (error) {
86
+ import_utils.logger.error("Error executing message filter:", error);
87
+ throw error;
88
+ }
89
+ import_utils.logger.debug(`\u2705 [filter-messages] Filter passed, continuing chain`);
90
+ await context.next?.();
91
+ }
92
+ };
93
+ }
94
+
95
+ // src/behaviors/react-with.ts
96
+ var import_utils2 = require("@hybrd/utils");
97
+ var import_xmtp2 = require("@hybrd/xmtp");
98
+ function reactWith(reaction, options = {}) {
99
+ return {
100
+ id: `react-with-${reaction}`,
101
+ config: {
102
+ enabled: options.enabled ?? true,
103
+ config: {
104
+ reaction,
105
+ reactToAll: options.reactToAll ?? true
106
+ }
107
+ },
108
+ async before(context) {
109
+ if (!this.config.enabled) return;
110
+ try {
111
+ const reactionMessage = {
112
+ schema: "unicode",
113
+ reference: context.message.id,
114
+ action: "added",
115
+ contentType: import_xmtp2.ContentTypeReaction,
116
+ content: reaction
117
+ };
118
+ await context.conversation.send(reactionMessage, import_xmtp2.ContentTypeReaction);
119
+ import_utils2.logger.debug(
120
+ `\u2705 [react-with] Reacted with ${reaction} to message ${context.message.id}`
121
+ );
122
+ } catch (error) {
123
+ import_utils2.logger.error(
124
+ `\u274C [react-with] Failed to add reaction ${reaction}:`,
125
+ error
126
+ );
127
+ }
128
+ }
129
+ };
130
+ }
131
+
132
+ // src/behaviors/threaded-reply.ts
133
+ function threadedReply(options = {}) {
134
+ return {
135
+ id: "threaded-reply",
136
+ config: {
137
+ enabled: options.enabled ?? true,
138
+ config: {
139
+ alwaysThread: true
140
+ }
141
+ },
142
+ async after(context) {
143
+ if (!this.config.enabled) return;
144
+ if (!context.sendOptions) {
145
+ context.sendOptions = {};
146
+ }
147
+ context.sendOptions.threaded = true;
148
+ }
149
+ };
150
+ }
151
+
152
+ // src/behaviors/index.ts
153
+ var import_types = require("@hybrd/types");
154
+ // Annotate the CommonJS export names for ESM import in node:
155
+ 0 && (module.exports = {
156
+ BehaviorRegistryImpl,
157
+ filter,
158
+ filterMessages,
159
+ reactWith,
160
+ threadedReply
161
+ });
162
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/behaviors/index.ts","../../src/behaviors/filter-messages.ts","../../src/behaviors/react-with.ts","../../src/behaviors/threaded-reply.ts"],"sourcesContent":["// Re-export XMTP Agent SDK filters for convenience\nexport { filter } from \"@hybrd/xmtp\"\n\nexport * from \"./filter-messages\"\nexport * from \"./react-with\"\nexport * from \"./threaded-reply\"\n\n// Re-export behavior types for convenience\nexport { BehaviorRegistryImpl } from \"@hybrd/types\"\nexport type {\n\tBehavior,\n\tBehaviorConfig,\n\tBehaviorContext,\n\tBehaviorObject,\n\tBehaviorRegistry\n} from \"@hybrd/types\"\n","import type { BehaviorContext, BehaviorObject } from \"@hybrd/types\"\nimport { logger } from \"@hybrd/utils\"\nimport { filter } from \"@hybrd/xmtp\"\n\n// Filter interface that matches XMTP SDK signatures\ninterface FilterAPI {\n\tfromSelf(): boolean\n\thasContent(): boolean\n\tisDM(): boolean\n\tisGroup(): boolean\n\tisGroupAdmin(): boolean\n\tisGroupSuperAdmin(): boolean\n\tisReaction(): boolean\n\tisRemoteAttachment(): boolean\n\tisReply(): boolean\n\tisText(): boolean\n\tisTextReply(): boolean\n\thasMention(mention: string): boolean\n}\n\nexport function filterMessages(\n\tfilters: (api: FilterAPI) => boolean\n): BehaviorObject {\n\treturn {\n\t\tid: \"filter-messages\",\n\t\tconfig: {\n\t\t\tenabled: true,\n\t\t\tconfig: {\n\t\t\t\tfilters: 1\n\t\t\t}\n\t\t},\n\t\tasync before(context: BehaviorContext) {\n\t\t\tconst messageContent =\n\t\t\t\ttypeof context.message.content === \"string\"\n\t\t\t\t\t? context.message.content.substring(0, 100)\n\t\t\t\t\t: String(context.message.content || \"unknown\")\n\t\t\tlogger.debug(\n\t\t\t\t`🔍 [filter-messages] Processing message: ${messageContent}...`\n\t\t\t)\n\n\t\t\t// Create filter API wrapper\n\t\t\tconst filterAPI: FilterAPI = {\n\t\t\t\tfromSelf: () =>\n\t\t\t\t\tfilter.fromSelf(context.message as any, context.client as any),\n\t\t\t\thasContent: () => filter.hasContent(context.message as any),\n\t\t\t\tisDM: () => filter.isDM(context.conversation as any),\n\t\t\t\tisGroup: () => filter.isGroup(context.conversation as any),\n\t\t\t\tisGroupAdmin: () =>\n\t\t\t\t\tfilter.isGroupAdmin(\n\t\t\t\t\t\tcontext.conversation as any,\n\t\t\t\t\t\tcontext.message as any\n\t\t\t\t\t),\n\t\t\t\tisGroupSuperAdmin: () =>\n\t\t\t\t\tfilter.isGroupSuperAdmin(\n\t\t\t\t\t\tcontext.conversation as any,\n\t\t\t\t\t\tcontext.message as any\n\t\t\t\t\t),\n\t\t\t\tisReaction: () => filter.isReaction(context.message as any),\n\t\t\t\tisRemoteAttachment: () =>\n\t\t\t\t\tfilter.isRemoteAttachment(context.message as any),\n\t\t\t\tisReply: () => filter.isReply(context.message as any),\n\t\t\t\tisText: () => filter.isText(context.message as any),\n\t\t\t\tisTextReply: () => filter.isTextReply(context.message as any),\n\t\t\t\thasMention: (mention: string) => {\n\t\t\t\t\tconst content =\n\t\t\t\t\t\ttypeof context.message.content === \"string\"\n\t\t\t\t\t\t\t? context.message.content\n\t\t\t\t\t\t\t: String(context.message.content || \"\")\n\t\t\t\t\treturn content.includes(mention)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst passes = filters(filterAPI)\n\n\t\t\t\tif (!passes) {\n\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t`🔇 [filter-messages] Filter failed - message filtered out`\n\t\t\t\t\t)\n\t\t\t\t\t// Message filtered, set flag and stop the chain\n\t\t\t\t\tif (!context.sendOptions) {\n\t\t\t\t\t\tcontext.sendOptions = {}\n\t\t\t\t\t}\n\t\t\t\t\tcontext.sendOptions.filtered = true\n\t\t\t\t\t// Don't call next() - this stops the middleware chain\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tlogger.debug(`✅ [filter-messages] Filter passed`)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(\"Error executing message filter:\", error)\n\t\t\t\tthrow error // Re-throw to propagate the error\n\t\t\t}\n\n\t\t\tlogger.debug(`✅ [filter-messages] Filter passed, continuing chain`)\n\t\t\t// Filter passed, continue to next behavior\n\t\t\tawait context.next?.()\n\t\t}\n\t}\n}\n","import type { BehaviorContext, BehaviorObject } from \"@hybrd/types\"\nimport { logger } from \"@hybrd/utils\"\nimport { ContentTypeReaction } from \"@hybrd/xmtp\"\n\nexport interface ReactWithOptions {\n\treactToAll?: boolean\n\tenabled?: boolean\n}\n\nexport function reactWith(\n\treaction: string,\n\toptions: ReactWithOptions = {}\n): BehaviorObject {\n\treturn {\n\t\tid: `react-with-${reaction}`,\n\t\tconfig: {\n\t\t\tenabled: options.enabled ?? true,\n\t\t\tconfig: {\n\t\t\t\treaction,\n\t\t\t\treactToAll: options.reactToAll ?? true\n\t\t\t}\n\t\t},\n\t\tasync before(context: BehaviorContext) {\n\t\t\tif (!this.config.enabled) return\n\n\t\t\ttry {\n\t\t\t\tconst reactionMessage = {\n\t\t\t\t\tschema: \"unicode\",\n\t\t\t\t\treference: context.message.id,\n\t\t\t\t\taction: \"added\",\n\t\t\t\t\tcontentType: ContentTypeReaction,\n\t\t\t\t\tcontent: reaction\n\t\t\t\t}\n\n\t\t\t\tawait context.conversation.send(reactionMessage, ContentTypeReaction)\n\t\t\t\tlogger.debug(\n\t\t\t\t\t`✅ [react-with] Reacted with ${reaction} to message ${context.message.id}`\n\t\t\t\t)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(\n\t\t\t\t\t`❌ [react-with] Failed to add reaction ${reaction}:`,\n\t\t\t\t\terror\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n}\n","import type { BehaviorContext, BehaviorObject } from \"@hybrd/types\"\n\nexport interface ThreadedReplyOptions {\n\tenabled?: boolean\n}\n\nexport function threadedReply(\n\toptions: ThreadedReplyOptions = {}\n): BehaviorObject {\n\treturn {\n\t\tid: \"threaded-reply\",\n\t\tconfig: {\n\t\t\tenabled: options.enabled ?? true,\n\t\t\tconfig: {\n\t\t\t\talwaysThread: true\n\t\t\t}\n\t\t},\n\t\tasync after(context: BehaviorContext) {\n\t\t\tif (!this.config.enabled) return\n\n\t\t\tif (!context.sendOptions) {\n\t\t\t\tcontext.sendOptions = {}\n\t\t\t}\n\t\t\tcontext.sendOptions.threaded = true\n\t\t}\n\t}\n}\n"],"mappings":"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","names":["import_xmtp","import_utils","import_xmtp"]}
@@ -0,0 +1,32 @@
1
+ export { filter } from '@hybrd/xmtp';
2
+ import { BehaviorObject } from '@hybrd/types';
3
+ export { Behavior, BehaviorConfig, BehaviorContext, BehaviorObject, BehaviorRegistry, BehaviorRegistryImpl } from '@hybrd/types';
4
+
5
+ interface FilterAPI {
6
+ fromSelf(): boolean;
7
+ hasContent(): boolean;
8
+ isDM(): boolean;
9
+ isGroup(): boolean;
10
+ isGroupAdmin(): boolean;
11
+ isGroupSuperAdmin(): boolean;
12
+ isReaction(): boolean;
13
+ isRemoteAttachment(): boolean;
14
+ isReply(): boolean;
15
+ isText(): boolean;
16
+ isTextReply(): boolean;
17
+ hasMention(mention: string): boolean;
18
+ }
19
+ declare function filterMessages(filters: (api: FilterAPI) => boolean): BehaviorObject;
20
+
21
+ interface ReactWithOptions {
22
+ reactToAll?: boolean;
23
+ enabled?: boolean;
24
+ }
25
+ declare function reactWith(reaction: string, options?: ReactWithOptions): BehaviorObject;
26
+
27
+ interface ThreadedReplyOptions {
28
+ enabled?: boolean;
29
+ }
30
+ declare function threadedReply(options?: ThreadedReplyOptions): BehaviorObject;
31
+
32
+ export { type ReactWithOptions, type ThreadedReplyOptions, filterMessages, reactWith, threadedReply };
@@ -0,0 +1,32 @@
1
+ export { filter } from '@hybrd/xmtp';
2
+ import { BehaviorObject } from '@hybrd/types';
3
+ export { Behavior, BehaviorConfig, BehaviorContext, BehaviorObject, BehaviorRegistry, BehaviorRegistryImpl } from '@hybrd/types';
4
+
5
+ interface FilterAPI {
6
+ fromSelf(): boolean;
7
+ hasContent(): boolean;
8
+ isDM(): boolean;
9
+ isGroup(): boolean;
10
+ isGroupAdmin(): boolean;
11
+ isGroupSuperAdmin(): boolean;
12
+ isReaction(): boolean;
13
+ isRemoteAttachment(): boolean;
14
+ isReply(): boolean;
15
+ isText(): boolean;
16
+ isTextReply(): boolean;
17
+ hasMention(mention: string): boolean;
18
+ }
19
+ declare function filterMessages(filters: (api: FilterAPI) => boolean): BehaviorObject;
20
+
21
+ interface ReactWithOptions {
22
+ reactToAll?: boolean;
23
+ enabled?: boolean;
24
+ }
25
+ declare function reactWith(reaction: string, options?: ReactWithOptions): BehaviorObject;
26
+
27
+ interface ThreadedReplyOptions {
28
+ enabled?: boolean;
29
+ }
30
+ declare function threadedReply(options?: ThreadedReplyOptions): BehaviorObject;
31
+
32
+ export { type ReactWithOptions, type ThreadedReplyOptions, filterMessages, reactWith, threadedReply };
@@ -0,0 +1,133 @@
1
+ // src/behaviors/index.ts
2
+ import { filter as filter2 } from "@hybrd/xmtp";
3
+
4
+ // src/behaviors/filter-messages.ts
5
+ import { logger } from "@hybrd/utils";
6
+ import { filter } from "@hybrd/xmtp";
7
+ function filterMessages(filters) {
8
+ return {
9
+ id: "filter-messages",
10
+ config: {
11
+ enabled: true,
12
+ config: {
13
+ filters: 1
14
+ }
15
+ },
16
+ async before(context) {
17
+ const messageContent = typeof context.message.content === "string" ? context.message.content.substring(0, 100) : String(context.message.content || "unknown");
18
+ logger.debug(
19
+ `\u{1F50D} [filter-messages] Processing message: ${messageContent}...`
20
+ );
21
+ const filterAPI = {
22
+ fromSelf: () => filter.fromSelf(context.message, context.client),
23
+ hasContent: () => filter.hasContent(context.message),
24
+ isDM: () => filter.isDM(context.conversation),
25
+ isGroup: () => filter.isGroup(context.conversation),
26
+ isGroupAdmin: () => filter.isGroupAdmin(
27
+ context.conversation,
28
+ context.message
29
+ ),
30
+ isGroupSuperAdmin: () => filter.isGroupSuperAdmin(
31
+ context.conversation,
32
+ context.message
33
+ ),
34
+ isReaction: () => filter.isReaction(context.message),
35
+ isRemoteAttachment: () => filter.isRemoteAttachment(context.message),
36
+ isReply: () => filter.isReply(context.message),
37
+ isText: () => filter.isText(context.message),
38
+ isTextReply: () => filter.isTextReply(context.message),
39
+ hasMention: (mention) => {
40
+ const content = typeof context.message.content === "string" ? context.message.content : String(context.message.content || "");
41
+ return content.includes(mention);
42
+ }
43
+ };
44
+ try {
45
+ const passes = filters(filterAPI);
46
+ if (!passes) {
47
+ logger.debug(
48
+ `\u{1F507} [filter-messages] Filter failed - message filtered out`
49
+ );
50
+ if (!context.sendOptions) {
51
+ context.sendOptions = {};
52
+ }
53
+ context.sendOptions.filtered = true;
54
+ return;
55
+ }
56
+ logger.debug(`\u2705 [filter-messages] Filter passed`);
57
+ } catch (error) {
58
+ logger.error("Error executing message filter:", error);
59
+ throw error;
60
+ }
61
+ logger.debug(`\u2705 [filter-messages] Filter passed, continuing chain`);
62
+ await context.next?.();
63
+ }
64
+ };
65
+ }
66
+
67
+ // src/behaviors/react-with.ts
68
+ import { logger as logger2 } from "@hybrd/utils";
69
+ import { ContentTypeReaction } from "@hybrd/xmtp";
70
+ function reactWith(reaction, options = {}) {
71
+ return {
72
+ id: `react-with-${reaction}`,
73
+ config: {
74
+ enabled: options.enabled ?? true,
75
+ config: {
76
+ reaction,
77
+ reactToAll: options.reactToAll ?? true
78
+ }
79
+ },
80
+ async before(context) {
81
+ if (!this.config.enabled) return;
82
+ try {
83
+ const reactionMessage = {
84
+ schema: "unicode",
85
+ reference: context.message.id,
86
+ action: "added",
87
+ contentType: ContentTypeReaction,
88
+ content: reaction
89
+ };
90
+ await context.conversation.send(reactionMessage, ContentTypeReaction);
91
+ logger2.debug(
92
+ `\u2705 [react-with] Reacted with ${reaction} to message ${context.message.id}`
93
+ );
94
+ } catch (error) {
95
+ logger2.error(
96
+ `\u274C [react-with] Failed to add reaction ${reaction}:`,
97
+ error
98
+ );
99
+ }
100
+ }
101
+ };
102
+ }
103
+
104
+ // src/behaviors/threaded-reply.ts
105
+ function threadedReply(options = {}) {
106
+ return {
107
+ id: "threaded-reply",
108
+ config: {
109
+ enabled: options.enabled ?? true,
110
+ config: {
111
+ alwaysThread: true
112
+ }
113
+ },
114
+ async after(context) {
115
+ if (!this.config.enabled) return;
116
+ if (!context.sendOptions) {
117
+ context.sendOptions = {};
118
+ }
119
+ context.sendOptions.threaded = true;
120
+ }
121
+ };
122
+ }
123
+
124
+ // src/behaviors/index.ts
125
+ import { BehaviorRegistryImpl } from "@hybrd/types";
126
+ export {
127
+ BehaviorRegistryImpl,
128
+ filter2 as filter,
129
+ filterMessages,
130
+ reactWith,
131
+ threadedReply
132
+ };
133
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/behaviors/index.ts","../../src/behaviors/filter-messages.ts","../../src/behaviors/react-with.ts","../../src/behaviors/threaded-reply.ts"],"sourcesContent":["// Re-export XMTP Agent SDK filters for convenience\nexport { filter } from \"@hybrd/xmtp\"\n\nexport * from \"./filter-messages\"\nexport * from \"./react-with\"\nexport * from \"./threaded-reply\"\n\n// Re-export behavior types for convenience\nexport { BehaviorRegistryImpl } from \"@hybrd/types\"\nexport type {\n\tBehavior,\n\tBehaviorConfig,\n\tBehaviorContext,\n\tBehaviorObject,\n\tBehaviorRegistry\n} from \"@hybrd/types\"\n","import type { BehaviorContext, BehaviorObject } from \"@hybrd/types\"\nimport { logger } from \"@hybrd/utils\"\nimport { filter } from \"@hybrd/xmtp\"\n\n// Filter interface that matches XMTP SDK signatures\ninterface FilterAPI {\n\tfromSelf(): boolean\n\thasContent(): boolean\n\tisDM(): boolean\n\tisGroup(): boolean\n\tisGroupAdmin(): boolean\n\tisGroupSuperAdmin(): boolean\n\tisReaction(): boolean\n\tisRemoteAttachment(): boolean\n\tisReply(): boolean\n\tisText(): boolean\n\tisTextReply(): boolean\n\thasMention(mention: string): boolean\n}\n\nexport function filterMessages(\n\tfilters: (api: FilterAPI) => boolean\n): BehaviorObject {\n\treturn {\n\t\tid: \"filter-messages\",\n\t\tconfig: {\n\t\t\tenabled: true,\n\t\t\tconfig: {\n\t\t\t\tfilters: 1\n\t\t\t}\n\t\t},\n\t\tasync before(context: BehaviorContext) {\n\t\t\tconst messageContent =\n\t\t\t\ttypeof context.message.content === \"string\"\n\t\t\t\t\t? context.message.content.substring(0, 100)\n\t\t\t\t\t: String(context.message.content || \"unknown\")\n\t\t\tlogger.debug(\n\t\t\t\t`🔍 [filter-messages] Processing message: ${messageContent}...`\n\t\t\t)\n\n\t\t\t// Create filter API wrapper\n\t\t\tconst filterAPI: FilterAPI = {\n\t\t\t\tfromSelf: () =>\n\t\t\t\t\tfilter.fromSelf(context.message as any, context.client as any),\n\t\t\t\thasContent: () => filter.hasContent(context.message as any),\n\t\t\t\tisDM: () => filter.isDM(context.conversation as any),\n\t\t\t\tisGroup: () => filter.isGroup(context.conversation as any),\n\t\t\t\tisGroupAdmin: () =>\n\t\t\t\t\tfilter.isGroupAdmin(\n\t\t\t\t\t\tcontext.conversation as any,\n\t\t\t\t\t\tcontext.message as any\n\t\t\t\t\t),\n\t\t\t\tisGroupSuperAdmin: () =>\n\t\t\t\t\tfilter.isGroupSuperAdmin(\n\t\t\t\t\t\tcontext.conversation as any,\n\t\t\t\t\t\tcontext.message as any\n\t\t\t\t\t),\n\t\t\t\tisReaction: () => filter.isReaction(context.message as any),\n\t\t\t\tisRemoteAttachment: () =>\n\t\t\t\t\tfilter.isRemoteAttachment(context.message as any),\n\t\t\t\tisReply: () => filter.isReply(context.message as any),\n\t\t\t\tisText: () => filter.isText(context.message as any),\n\t\t\t\tisTextReply: () => filter.isTextReply(context.message as any),\n\t\t\t\thasMention: (mention: string) => {\n\t\t\t\t\tconst content =\n\t\t\t\t\t\ttypeof context.message.content === \"string\"\n\t\t\t\t\t\t\t? context.message.content\n\t\t\t\t\t\t\t: String(context.message.content || \"\")\n\t\t\t\t\treturn content.includes(mention)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst passes = filters(filterAPI)\n\n\t\t\t\tif (!passes) {\n\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t`🔇 [filter-messages] Filter failed - message filtered out`\n\t\t\t\t\t)\n\t\t\t\t\t// Message filtered, set flag and stop the chain\n\t\t\t\t\tif (!context.sendOptions) {\n\t\t\t\t\t\tcontext.sendOptions = {}\n\t\t\t\t\t}\n\t\t\t\t\tcontext.sendOptions.filtered = true\n\t\t\t\t\t// Don't call next() - this stops the middleware chain\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tlogger.debug(`✅ [filter-messages] Filter passed`)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(\"Error executing message filter:\", error)\n\t\t\t\tthrow error // Re-throw to propagate the error\n\t\t\t}\n\n\t\t\tlogger.debug(`✅ [filter-messages] Filter passed, continuing chain`)\n\t\t\t// Filter passed, continue to next behavior\n\t\t\tawait context.next?.()\n\t\t}\n\t}\n}\n","import type { BehaviorContext, BehaviorObject } from \"@hybrd/types\"\nimport { logger } from \"@hybrd/utils\"\nimport { ContentTypeReaction } from \"@hybrd/xmtp\"\n\nexport interface ReactWithOptions {\n\treactToAll?: boolean\n\tenabled?: boolean\n}\n\nexport function reactWith(\n\treaction: string,\n\toptions: ReactWithOptions = {}\n): BehaviorObject {\n\treturn {\n\t\tid: `react-with-${reaction}`,\n\t\tconfig: {\n\t\t\tenabled: options.enabled ?? true,\n\t\t\tconfig: {\n\t\t\t\treaction,\n\t\t\t\treactToAll: options.reactToAll ?? true\n\t\t\t}\n\t\t},\n\t\tasync before(context: BehaviorContext) {\n\t\t\tif (!this.config.enabled) return\n\n\t\t\ttry {\n\t\t\t\tconst reactionMessage = {\n\t\t\t\t\tschema: \"unicode\",\n\t\t\t\t\treference: context.message.id,\n\t\t\t\t\taction: \"added\",\n\t\t\t\t\tcontentType: ContentTypeReaction,\n\t\t\t\t\tcontent: reaction\n\t\t\t\t}\n\n\t\t\t\tawait context.conversation.send(reactionMessage, ContentTypeReaction)\n\t\t\t\tlogger.debug(\n\t\t\t\t\t`✅ [react-with] Reacted with ${reaction} to message ${context.message.id}`\n\t\t\t\t)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(\n\t\t\t\t\t`❌ [react-with] Failed to add reaction ${reaction}:`,\n\t\t\t\t\terror\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n}\n","import type { BehaviorContext, BehaviorObject } from \"@hybrd/types\"\n\nexport interface ThreadedReplyOptions {\n\tenabled?: boolean\n}\n\nexport function threadedReply(\n\toptions: ThreadedReplyOptions = {}\n): BehaviorObject {\n\treturn {\n\t\tid: \"threaded-reply\",\n\t\tconfig: {\n\t\t\tenabled: options.enabled ?? true,\n\t\t\tconfig: {\n\t\t\t\talwaysThread: true\n\t\t\t}\n\t\t},\n\t\tasync after(context: BehaviorContext) {\n\t\t\tif (!this.config.enabled) return\n\n\t\t\tif (!context.sendOptions) {\n\t\t\t\tcontext.sendOptions = {}\n\t\t\t}\n\t\t\tcontext.sendOptions.threaded = true\n\t\t}\n\t}\n}\n"],"mappings":"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","names":["filter","logger"]}