@teneo-protocol/sdk 1.0.0 → 1.0.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 (97) hide show
  1. package/dist/core/websocket-client.d.ts +1 -0
  2. package/dist/core/websocket-client.d.ts.map +1 -1
  3. package/dist/core/websocket-client.js +12 -1
  4. package/dist/core/websocket-client.js.map +1 -1
  5. package/dist/handlers/message-handlers/regular-message-handler.d.ts.map +1 -1
  6. package/dist/handlers/message-handlers/regular-message-handler.js +1 -0
  7. package/dist/handlers/message-handlers/regular-message-handler.js.map +1 -1
  8. package/dist/managers/message-router.d.ts +1 -1
  9. package/dist/managers/message-router.d.ts.map +1 -1
  10. package/dist/managers/message-router.js +41 -4
  11. package/dist/managers/message-router.js.map +1 -1
  12. package/dist/managers/room-manager.d.ts.map +1 -1
  13. package/dist/managers/room-manager.js +1 -1
  14. package/dist/managers/room-manager.js.map +1 -1
  15. package/dist/teneo-sdk.d.ts +29 -1
  16. package/dist/teneo-sdk.d.ts.map +1 -1
  17. package/dist/teneo-sdk.js +29 -6
  18. package/dist/teneo-sdk.js.map +1 -1
  19. package/dist/types/config.d.ts.map +1 -1
  20. package/dist/types/config.js +1 -1
  21. package/dist/types/config.js.map +1 -1
  22. package/dist/types/validation.d.ts.map +1 -1
  23. package/dist/types/validation.js +1 -1
  24. package/dist/types/validation.js.map +1 -1
  25. package/dist/utils/bounded-queue.d.ts +1 -1
  26. package/dist/utils/bounded-queue.js +6 -6
  27. package/dist/utils/circuit-breaker.d.ts.map +1 -1
  28. package/dist/utils/circuit-breaker.js.map +1 -1
  29. package/dist/utils/event-waiter.d.ts.map +1 -1
  30. package/dist/utils/event-waiter.js +2 -1
  31. package/dist/utils/event-waiter.js.map +1 -1
  32. package/dist/utils/rate-limiter.d.ts.map +1 -1
  33. package/dist/utils/rate-limiter.js +4 -6
  34. package/dist/utils/rate-limiter.js.map +1 -1
  35. package/dist/utils/secure-private-key.d.ts.map +1 -1
  36. package/dist/utils/secure-private-key.js +9 -15
  37. package/dist/utils/secure-private-key.js.map +1 -1
  38. package/dist/utils/signature-verifier.d.ts +2 -2
  39. package/dist/utils/signature-verifier.d.ts.map +1 -1
  40. package/dist/utils/signature-verifier.js +5 -5
  41. package/dist/utils/signature-verifier.js.map +1 -1
  42. package/examples/claude-agent-x-follower/.env.example +117 -0
  43. package/examples/claude-agent-x-follower/QUICKSTART.md +243 -0
  44. package/examples/claude-agent-x-follower/README.md +540 -0
  45. package/examples/claude-agent-x-follower/index.ts +248 -0
  46. package/examples/claude-agent-x-follower/package.json +37 -0
  47. package/examples/claude-agent-x-follower/tsconfig.json +20 -0
  48. package/examples/n8n-teneo/.env.example +127 -0
  49. package/examples/n8n-teneo/Dockerfile +42 -0
  50. package/examples/n8n-teneo/README.md +564 -0
  51. package/examples/n8n-teneo/docker-compose.yml +71 -0
  52. package/examples/n8n-teneo/index.ts +177 -0
  53. package/examples/n8n-teneo/package.json +22 -0
  54. package/examples/n8n-teneo/tsconfig.json +12 -0
  55. package/examples/n8n-teneo/workflows/x-timeline.json +66 -0
  56. package/examples/openai-teneo/.env.example +130 -0
  57. package/examples/openai-teneo/README.md +635 -0
  58. package/examples/openai-teneo/index.ts +280 -0
  59. package/examples/openai-teneo/package.json +24 -0
  60. package/examples/openai-teneo/tsconfig.json +16 -0
  61. package/examples/production-dashboard/.env.example +5 -3
  62. package/examples/production-dashboard/README.md +762 -0
  63. package/examples/production-dashboard/pnpm-lock.yaml +92 -0
  64. package/examples/production-dashboard/public/dashboard.html +84 -10
  65. package/examples/production-dashboard/server.ts +83 -9
  66. package/examples/usage/.env.example +17 -0
  67. package/examples/usage/01-connect.ts +116 -0
  68. package/examples/usage/02-list-agents.ts +153 -0
  69. package/examples/usage/03-pick-agent.ts +201 -0
  70. package/examples/usage/04-find-by-capability.ts +237 -0
  71. package/examples/usage/05-webhook-example.ts +319 -0
  72. package/examples/usage/06-simple-api-server.ts +396 -0
  73. package/examples/usage/07-event-listener.ts +402 -0
  74. package/examples/usage/README.md +383 -0
  75. package/examples/usage/package.json +42 -0
  76. package/package.json +5 -3
  77. package/src/core/websocket-client.ts +17 -7
  78. package/src/formatters/response-formatter.test.ts +8 -2
  79. package/src/handlers/message-handlers/regular-message-handler.ts +1 -0
  80. package/src/handlers/webhook-handler.test.ts +13 -10
  81. package/src/managers/message-router.ts +48 -6
  82. package/src/managers/room-manager.ts +9 -2
  83. package/src/teneo-sdk.ts +39 -7
  84. package/src/types/config.ts +3 -1
  85. package/src/types/validation.ts +4 -1
  86. package/src/utils/bounded-queue.ts +9 -9
  87. package/src/utils/circuit-breaker.ts +4 -1
  88. package/src/utils/deduplication-cache.test.ts +2 -6
  89. package/src/utils/event-waiter.test.ts +4 -1
  90. package/src/utils/event-waiter.ts +5 -7
  91. package/src/utils/rate-limiter.test.ts +5 -17
  92. package/src/utils/rate-limiter.ts +6 -9
  93. package/src/utils/secure-private-key.test.ts +66 -59
  94. package/src/utils/secure-private-key.ts +10 -16
  95. package/src/utils/signature-verifier.test.ts +75 -70
  96. package/src/utils/signature-verifier.ts +7 -8
  97. package/src/utils/ssrf-validator.test.ts +3 -3
@@ -5,7 +5,14 @@
5
5
 
6
6
  import { EventEmitter } from "eventemitter3";
7
7
  import { WebSocketClient } from "../core/websocket-client";
8
- import { Room, createSubscribe, createUnsubscribe, createListRooms, Logger, RoomInfo } from "../types";
8
+ import {
9
+ Room,
10
+ createSubscribe,
11
+ createUnsubscribe,
12
+ createListRooms,
13
+ Logger,
14
+ RoomInfo
15
+ } from "../types";
9
16
  import { SDKEvents, SDKError } from "../types/events";
10
17
  import { ErrorCode } from "../types/error-codes";
11
18
  import { RoomIdSchema } from "../types/validation";
@@ -98,7 +105,7 @@ export class RoomManager extends EventEmitter<SDKEvents> {
98
105
  */
99
106
  public updateSubscriptions(subscriptions: string[]): void {
100
107
  this.subscribedRooms.clear();
101
- subscriptions.forEach(roomId => this.subscribedRooms.add(roomId));
108
+ subscriptions.forEach((roomId) => this.subscribedRooms.add(roomId));
102
109
  this.logger.debug("RoomManager: Subscriptions updated", {
103
110
  count: subscriptions.length,
104
111
  rooms: subscriptions
package/src/teneo-sdk.ts CHANGED
@@ -290,8 +290,11 @@ export class TeneoSDK extends EventEmitter<SDKEvents> {
290
290
  * });
291
291
  * ```
292
292
  */
293
- public async sendDirectCommand(command: AgentCommand): Promise<FormattedResponse | void> {
294
- return this.messages.sendDirectCommand(command);
293
+ public async sendDirectCommand(
294
+ command: AgentCommand,
295
+ waitForResponse: boolean = false
296
+ ): Promise<FormattedResponse | void> {
297
+ return this.messages.sendDirectCommand(command, waitForResponse);
295
298
  }
296
299
 
297
300
  /**
@@ -719,6 +722,39 @@ export class TeneoSDK extends EventEmitter<SDKEvents> {
719
722
  };
720
723
  }
721
724
 
725
+ /**
726
+ * Gets the current message deduplication cache status (CB-4).
727
+ * Returns statistics about the deduplication cache including size, TTL, and capacity.
728
+ * Useful for monitoring deduplication behavior and cache health.
729
+ * Returns undefined if deduplication is not configured or disabled.
730
+ *
731
+ * @returns Deduplication cache status object, or undefined if not configured
732
+ * @returns {number} returns.cacheSize - Number of message IDs currently cached
733
+ * @returns {number} returns.ttl - Time-to-live for cache entries in milliseconds
734
+ * @returns {number} returns.maxSize - Maximum cache size capacity
735
+ *
736
+ * @example
737
+ * ```typescript
738
+ * const status = sdk.getDeduplicationStatus();
739
+ * if (status) {
740
+ * console.log(`Cache: ${status.cacheSize}/${status.maxSize}`);
741
+ * console.log(`Utilization: ${(status.cacheSize / status.maxSize * 100).toFixed(1)}%`);
742
+ * console.log(`TTL: ${status.ttl}ms`);
743
+ * } else {
744
+ * console.log('Deduplication not enabled');
745
+ * }
746
+ * ```
747
+ */
748
+ public getDeduplicationStatus():
749
+ | {
750
+ cacheSize: number;
751
+ ttl: number;
752
+ maxSize: number;
753
+ }
754
+ | undefined {
755
+ return this.wsClient.getDeduplicationStatus();
756
+ }
757
+
722
758
  /**
723
759
  * Retries all failed webhook deliveries in the queue.
724
760
  * Resets attempt counters and immediately attempts to deliver all failed webhooks.
@@ -953,14 +989,10 @@ export class TeneoSDK extends EventEmitter<SDKEvents> {
953
989
  this.agents.updateAgents(agents);
954
990
  });
955
991
 
956
- // Forward room events from WebSocketClient (emitted by handlers)
992
+ // Forward room events from WebSocketClient (emitted by room subscription handlers)
957
993
  this.wsClient.on("room:subscribed", (data) => this.emit("room:subscribed", data));
958
994
  this.wsClient.on("room:unsubscribed", (data) => this.emit("room:unsubscribed", data));
959
995
 
960
- // Forward room events from RoomManager (if any direct emissions are added later)
961
- this.rooms.on("room:subscribed", (data) => this.emit("room:subscribed", data));
962
- this.rooms.on("room:unsubscribed", (data) => this.emit("room:unsubscribed", data));
963
-
964
996
  // Forward webhook events from WebhookHandler
965
997
  this.webhookHandler.on("webhook:sent", (payload, url) =>
966
998
  this.emit("webhook:sent", payload, url)
@@ -47,7 +47,9 @@ const PrivateKeySchema = z.union([
47
47
  z.custom<SecurePrivateKey>(
48
48
  (val) => {
49
49
  // Check if it's a SecurePrivateKey instance
50
- return val && typeof val === 'object' && 'use' in val && 'destroy' in val && 'isDestroyed' in val;
50
+ return (
51
+ val && typeof val === "object" && "use" in val && "destroy" in val && "isDestroyed" in val
52
+ );
51
53
  },
52
54
  { message: "Must be a string or SecurePrivateKey instance" }
53
55
  )
@@ -24,7 +24,10 @@ export const RoomIdSchema = z
24
24
  .string()
25
25
  .min(1, "Room ID cannot be empty")
26
26
  .max(100, "Room ID must be 100 characters or less")
27
- .regex(/^[a-zA-Z0-9_\- ]+$/, "Room ID can only contain letters, numbers, spaces, dashes, and underscores")
27
+ .regex(
28
+ /^[a-zA-Z0-9_ -]+$/,
29
+ "Room ID can only contain letters, numbers, spaces, dashes, and underscores"
30
+ )
28
31
  .trim();
29
32
 
30
33
  /**
@@ -11,9 +11,9 @@
11
11
  * Strategy for handling queue overflow when at max capacity
12
12
  */
13
13
  export type OverflowStrategy =
14
- | 'drop-oldest' // Remove oldest item to make room (FIFO eviction)
15
- | 'drop-newest' // Reject new item, keep existing (preserve old data)
16
- | 'reject'; // Throw error, let caller handle (fail-fast)
14
+ | "drop-oldest" // Remove oldest item to make room (FIFO eviction)
15
+ | "drop-newest" // Reject new item, keep existing (preserve old data)
16
+ | "reject"; // Throw error, let caller handle (fail-fast)
17
17
 
18
18
  /**
19
19
  * A queue with a maximum size limit and configurable overflow behavior
@@ -48,10 +48,10 @@ export class BoundedQueue<T> {
48
48
  */
49
49
  constructor(
50
50
  private readonly maxSize: number,
51
- private readonly strategy: OverflowStrategy = 'drop-oldest'
51
+ private readonly strategy: OverflowStrategy = "drop-oldest"
52
52
  ) {
53
53
  if (maxSize < 1) {
54
- throw new Error('BoundedQueue maxSize must be at least 1');
54
+ throw new Error("BoundedQueue maxSize must be at least 1");
55
55
  }
56
56
  }
57
57
 
@@ -165,17 +165,17 @@ export class BoundedQueue<T> {
165
165
  */
166
166
  private handleOverflow(item: T): boolean {
167
167
  switch (this.strategy) {
168
- case 'drop-oldest':
168
+ case "drop-oldest":
169
169
  // Remove oldest item and add new one
170
170
  this.queue.shift();
171
171
  this.queue.push(item);
172
172
  return true;
173
173
 
174
- case 'drop-newest':
174
+ case "drop-newest":
175
175
  // Reject the new item
176
176
  return false;
177
177
 
178
- case 'reject':
178
+ case "reject":
179
179
  // Throw error - let caller handle
180
180
  throw new QueueOverflowError(
181
181
  `Queue is full (max size: ${this.maxSize}). Cannot add more items.`
@@ -195,7 +195,7 @@ export class BoundedQueue<T> {
195
195
  export class QueueOverflowError extends Error {
196
196
  constructor(message: string) {
197
197
  super(message);
198
- this.name = 'QueueOverflowError';
198
+ this.name = "QueueOverflowError";
199
199
 
200
200
  // Maintains proper stack trace for where error was thrown (V8 only)
201
201
  if (Error.captureStackTrace) {
@@ -31,7 +31,10 @@ export interface CircuitBreakerOptions {
31
31
  * Error thrown when circuit is open
32
32
  */
33
33
  export class CircuitBreakerError extends Error {
34
- constructor(message: string, public readonly state: CircuitState) {
34
+ constructor(
35
+ message: string,
36
+ public readonly state: CircuitState
37
+ ) {
35
38
  super(message);
36
39
  this.name = "CircuitBreakerError";
37
40
 
@@ -25,15 +25,11 @@ describe("DeduplicationCache", () => {
25
25
  });
26
26
 
27
27
  it("should throw error if TTL is less than 1000ms", () => {
28
- expect(() => new DeduplicationCache(999)).toThrow(
29
- "TTL must be at least 1000ms"
30
- );
28
+ expect(() => new DeduplicationCache(999)).toThrow("TTL must be at least 1000ms");
31
29
  });
32
30
 
33
31
  it("should throw error if maxSize is less than 1", () => {
34
- expect(() => new DeduplicationCache(5000, 0)).toThrow(
35
- "maxSize must be at least 1"
36
- );
32
+ expect(() => new DeduplicationCache(5000, 0)).toThrow("maxSize must be at least 1");
37
33
  });
38
34
  });
39
35
 
@@ -376,6 +376,9 @@ describe("waitForAllEvents", () => {
376
376
  emitter.emit("data", { id: 1, value: "first" });
377
377
 
378
378
  const results = await promise;
379
- expect(results).toEqual([{ id: 1, value: "first" }, { id: 2, value: "second" }]);
379
+ expect(results).toEqual([
380
+ { id: 1, value: "first" },
381
+ { id: 2, value: "second" }
382
+ ]);
380
383
  });
381
384
  });
@@ -102,7 +102,9 @@ export async function waitForEvent<T = any>(
102
102
  // Timeout handler - rejects after timeout
103
103
  timeoutHandle = setTimeout(() => {
104
104
  cleanup();
105
- const message = options.timeoutMessage || `Timeout waiting for event '${eventName}' after ${options.timeout}ms`;
105
+ const message =
106
+ options.timeoutMessage ||
107
+ `Timeout waiting for event '${eventName}' after ${options.timeout}ms`;
106
108
  reject(new Error(message));
107
109
  }, options.timeout);
108
110
 
@@ -135,9 +137,7 @@ export async function waitForAnyEvent<T = any>(
135
137
  }>
136
138
  ): Promise<T> {
137
139
  return Promise.race(
138
- waiters.map(({ emitter, eventName, options }) =>
139
- waitForEvent<T>(emitter, eventName, options)
140
- )
140
+ waiters.map(({ emitter, eventName, options }) => waitForEvent<T>(emitter, eventName, options))
141
141
  );
142
142
  }
143
143
 
@@ -165,8 +165,6 @@ export async function waitForAllEvents<T = any>(
165
165
  }>
166
166
  ): Promise<T[]> {
167
167
  return Promise.all(
168
- waiters.map(({ emitter, eventName, options }) =>
169
- waitForEvent<T>(emitter, eventName, options)
170
- )
168
+ waiters.map(({ emitter, eventName, options }) => waitForEvent<T>(emitter, eventName, options))
171
169
  );
172
170
  }
@@ -17,21 +17,15 @@ describe("TokenBucketRateLimiter", () => {
17
17
  });
18
18
 
19
19
  it("should throw error if tokensPerSecond is less than 1", () => {
20
- expect(() => new TokenBucketRateLimiter(0, 10)).toThrow(
21
- "tokensPerSecond must be at least 1"
22
- );
20
+ expect(() => new TokenBucketRateLimiter(0, 10)).toThrow("tokensPerSecond must be at least 1");
23
21
  expect(() => new TokenBucketRateLimiter(-1, 10)).toThrow(
24
22
  "tokensPerSecond must be at least 1"
25
23
  );
26
24
  });
27
25
 
28
26
  it("should throw error if maxBurst is less than 1", () => {
29
- expect(() => new TokenBucketRateLimiter(10, 0)).toThrow(
30
- "maxBurst must be at least 1"
31
- );
32
- expect(() => new TokenBucketRateLimiter(10, -1)).toThrow(
33
- "maxBurst must be at least 1"
34
- );
27
+ expect(() => new TokenBucketRateLimiter(10, 0)).toThrow("maxBurst must be at least 1");
28
+ expect(() => new TokenBucketRateLimiter(10, -1)).toThrow("maxBurst must be at least 1");
35
29
  });
36
30
  });
37
31
 
@@ -159,9 +153,7 @@ describe("TokenBucketRateLimiter", () => {
159
153
  // Reset and try again to verify error message
160
154
  limiter.reset();
161
155
  limiter.tryConsume();
162
- await expect(limiter.consume(10)).rejects.toThrow(
163
- /Rate limit timeout/
164
- );
156
+ await expect(limiter.consume(10)).rejects.toThrow(/Rate limit timeout/);
165
157
  });
166
158
 
167
159
  it("should succeed within timeout if token becomes available", async () => {
@@ -309,11 +301,7 @@ describe("TokenBucketRateLimiter", () => {
309
301
  limiter.tryConsume();
310
302
 
311
303
  // Start multiple blocking consume calls
312
- const promises = [
313
- limiter.consume(1000),
314
- limiter.consume(1000),
315
- limiter.consume(1000)
316
- ];
304
+ const promises = [limiter.consume(1000), limiter.consume(1000), limiter.consume(1000)];
317
305
 
318
306
  // All should eventually succeed as tokens refill
319
307
  await expect(Promise.all(promises)).resolves.toBeDefined();
@@ -13,7 +13,7 @@
13
13
  export class RateLimitError extends Error {
14
14
  constructor(message: string) {
15
15
  super(message);
16
- this.name = 'RateLimitError';
16
+ this.name = "RateLimitError";
17
17
 
18
18
  if (Error.captureStackTrace) {
19
19
  Error.captureStackTrace(this, RateLimitError);
@@ -64,10 +64,10 @@ export class TokenBucketRateLimiter {
64
64
  private readonly maxBurst: number
65
65
  ) {
66
66
  if (tokensPerSecond < 1) {
67
- throw new Error('TokenBucketRateLimiter tokensPerSecond must be at least 1');
67
+ throw new Error("TokenBucketRateLimiter tokensPerSecond must be at least 1");
68
68
  }
69
69
  if (maxBurst < 1) {
70
- throw new Error('TokenBucketRateLimiter maxBurst must be at least 1');
70
+ throw new Error("TokenBucketRateLimiter maxBurst must be at least 1");
71
71
  }
72
72
 
73
73
  this.tokens = maxBurst; // Start with full bucket
@@ -131,17 +131,14 @@ export class TokenBucketRateLimiter {
131
131
  // Check timeout
132
132
  const elapsed = Date.now() - startTime;
133
133
  if (timeout !== undefined && elapsed >= timeout) {
134
- throw new RateLimitError(
135
- `Rate limit timeout: no token available after ${timeout}ms`
136
- );
134
+ throw new RateLimitError(`Rate limit timeout: no token available after ${timeout}ms`);
137
135
  }
138
136
 
139
137
  // Calculate wait time until next token
140
138
  // If timeout is specified, don't wait longer than remaining timeout
141
139
  const baseWaitTime = Math.min(this.refillInterval, 100);
142
- const waitTime = timeout !== undefined
143
- ? Math.min(baseWaitTime, timeout - elapsed)
144
- : baseWaitTime;
140
+ const waitTime =
141
+ timeout !== undefined ? Math.min(baseWaitTime, timeout - elapsed) : baseWaitTime;
145
142
 
146
143
  // If waitTime is very small or negative, check timeout immediately
147
144
  if (waitTime <= 0) {