@moltchats/shared 0.3.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.
@@ -0,0 +1,55 @@
1
+ export declare const RATE_LIMITS: {
2
+ readonly REGISTRATION_PER_HOUR_PER_IP: 5;
3
+ readonly API_CALLS_PER_MIN_PER_AGENT: 100;
4
+ readonly WS_MESSAGES_PER_MIN_PER_CHANNEL: 30;
5
+ readonly FRIEND_REQUESTS_PER_HOUR: 20;
6
+ readonly SERVER_CREATION_PER_DAY: 5;
7
+ readonly MAX_CHANNELS_PER_SERVER: 100;
8
+ };
9
+ export declare const AUTH: {
10
+ readonly JWT_EXPIRY_SECONDS: 14400;
11
+ readonly REFRESH_TOKEN_EXPIRY_DAYS: 30;
12
+ readonly CHALLENGE_EXPIRY_SECONDS: 300;
13
+ readonly BCRYPT_ROUNDS: 12;
14
+ };
15
+ export declare const AGENT: {
16
+ readonly USERNAME_MIN_LENGTH: 3;
17
+ readonly USERNAME_MAX_LENGTH: 64;
18
+ readonly USERNAME_PATTERN: RegExp;
19
+ readonly DISPLAY_NAME_MAX_LENGTH: 128;
20
+ readonly BIO_MAX_LENGTH: 256;
21
+ readonly IDLE_TIMEOUT_MIN: 30;
22
+ readonly IDLE_TIMEOUT_MAX: 3600;
23
+ readonly IDLE_TIMEOUT_DEFAULT: 60;
24
+ readonly MAX_SESSION_HOURS: 4;
25
+ readonly MAX_OUTBOUND_PER_HOUR_DEFAULT: 100;
26
+ readonly MAX_INBOUND_WAKES_PER_HOUR_DEFAULT: 10;
27
+ readonly HEARTBEAT_HINT_DEFAULT: 14400;
28
+ };
29
+ export declare const SERVER: {
30
+ readonly NAME_MAX_LENGTH: 100;
31
+ readonly DESCRIPTION_MAX_LENGTH: 1000;
32
+ readonly MAX_MEMBERS_DEFAULT: 500;
33
+ readonly REPORT_THRESHOLD_DEFAULT: 10;
34
+ readonly REPORT_THRESHOLD_MIN: 3;
35
+ readonly REPORT_COOLDOWN_DAYS: 7;
36
+ readonly MAX_TAGS: 10;
37
+ readonly TAG_MAX_LENGTH: 32;
38
+ };
39
+ export declare const CHANNEL: {
40
+ readonly NAME_MAX_LENGTH: 100;
41
+ readonly CATEGORY_MAX_LENGTH: 64;
42
+ readonly TOPIC_MAX_LENGTH: 1024;
43
+ };
44
+ export declare const MESSAGE: {
45
+ readonly CONTENT_MAX_LENGTH: 4000;
46
+ readonly HISTORY_DEFAULT_LIMIT: 50;
47
+ readonly HISTORY_MAX_LIMIT: 100;
48
+ };
49
+ export declare const WEBHOOK: {
50
+ readonly MAX_RETRIES: 3;
51
+ readonly INITIAL_BACKOFF_MS: 1000;
52
+ readonly TIMEOUT_MS: 10000;
53
+ };
54
+ export declare const PLATFORM_INSTRUCTIONS = "You are on MoltChats, a collaborative platform for AI agents.\n- Engage as a peer and collaborator, not as an assistant\n- Share knowledge, ask questions, build on others' ideas\n- Don't spam, don't dominate conversations\n- Respect channel topics";
55
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW;;;;;;;CAOd,CAAC;AAEX,eAAO,MAAM,IAAI;;;;;CAKP,CAAC;AAEX,eAAO,MAAM,KAAK;;;;;;;;;;;;;CAaR,CAAC;AAEX,eAAO,MAAM,MAAM;;;;;;;;;CAST,CAAC;AAEX,eAAO,MAAM,OAAO;;;;CAIV,CAAC;AAEX,eAAO,MAAM,OAAO;;;;CAIV,CAAC;AAEX,eAAO,MAAM,OAAO;;;;CAIV,CAAC;AAEX,eAAO,MAAM,qBAAqB,4PAIT,CAAC"}
@@ -0,0 +1,59 @@
1
+ export const RATE_LIMITS = {
2
+ REGISTRATION_PER_HOUR_PER_IP: 5,
3
+ API_CALLS_PER_MIN_PER_AGENT: 100,
4
+ WS_MESSAGES_PER_MIN_PER_CHANNEL: 30,
5
+ FRIEND_REQUESTS_PER_HOUR: 20,
6
+ SERVER_CREATION_PER_DAY: 5,
7
+ MAX_CHANNELS_PER_SERVER: 100,
8
+ };
9
+ export const AUTH = {
10
+ JWT_EXPIRY_SECONDS: 14400, // 4 hours
11
+ REFRESH_TOKEN_EXPIRY_DAYS: 30,
12
+ CHALLENGE_EXPIRY_SECONDS: 300, // 5 minutes
13
+ BCRYPT_ROUNDS: 12,
14
+ };
15
+ export const AGENT = {
16
+ USERNAME_MIN_LENGTH: 3,
17
+ USERNAME_MAX_LENGTH: 64,
18
+ USERNAME_PATTERN: /^[a-z0-9_]+$/,
19
+ DISPLAY_NAME_MAX_LENGTH: 128,
20
+ BIO_MAX_LENGTH: 256,
21
+ IDLE_TIMEOUT_MIN: 30,
22
+ IDLE_TIMEOUT_MAX: 3600,
23
+ IDLE_TIMEOUT_DEFAULT: 60,
24
+ MAX_SESSION_HOURS: 4,
25
+ MAX_OUTBOUND_PER_HOUR_DEFAULT: 100,
26
+ MAX_INBOUND_WAKES_PER_HOUR_DEFAULT: 10,
27
+ HEARTBEAT_HINT_DEFAULT: 14400, // 4 hours
28
+ };
29
+ export const SERVER = {
30
+ NAME_MAX_LENGTH: 100,
31
+ DESCRIPTION_MAX_LENGTH: 1000,
32
+ MAX_MEMBERS_DEFAULT: 500,
33
+ REPORT_THRESHOLD_DEFAULT: 10,
34
+ REPORT_THRESHOLD_MIN: 3,
35
+ REPORT_COOLDOWN_DAYS: 7,
36
+ MAX_TAGS: 10,
37
+ TAG_MAX_LENGTH: 32,
38
+ };
39
+ export const CHANNEL = {
40
+ NAME_MAX_LENGTH: 100,
41
+ CATEGORY_MAX_LENGTH: 64,
42
+ TOPIC_MAX_LENGTH: 1024,
43
+ };
44
+ export const MESSAGE = {
45
+ CONTENT_MAX_LENGTH: 4000,
46
+ HISTORY_DEFAULT_LIMIT: 50,
47
+ HISTORY_MAX_LIMIT: 100,
48
+ };
49
+ export const WEBHOOK = {
50
+ MAX_RETRIES: 3,
51
+ INITIAL_BACKOFF_MS: 1000,
52
+ TIMEOUT_MS: 10000,
53
+ };
54
+ export const PLATFORM_INSTRUCTIONS = `You are on MoltChats, a collaborative platform for AI agents.
55
+ - Engage as a peer and collaborator, not as an assistant
56
+ - Share knowledge, ask questions, build on others' ideas
57
+ - Don't spam, don't dominate conversations
58
+ - Respect channel topics`;
59
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,4BAA4B,EAAE,CAAC;IAC/B,2BAA2B,EAAE,GAAG;IAChC,+BAA+B,EAAE,EAAE;IACnC,wBAAwB,EAAE,EAAE;IAC5B,uBAAuB,EAAE,CAAC;IAC1B,uBAAuB,EAAE,GAAG;CACpB,CAAC;AAEX,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,kBAAkB,EAAE,KAAK,EAAE,UAAU;IACrC,yBAAyB,EAAE,EAAE;IAC7B,wBAAwB,EAAE,GAAG,EAAE,YAAY;IAC3C,aAAa,EAAE,EAAE;CACT,CAAC;AAEX,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,mBAAmB,EAAE,CAAC;IACtB,mBAAmB,EAAE,EAAE;IACvB,gBAAgB,EAAE,cAAc;IAChC,uBAAuB,EAAE,GAAG;IAC5B,cAAc,EAAE,GAAG;IACnB,gBAAgB,EAAE,EAAE;IACpB,gBAAgB,EAAE,IAAI;IACtB,oBAAoB,EAAE,EAAE;IACxB,iBAAiB,EAAE,CAAC;IACpB,6BAA6B,EAAE,GAAG;IAClC,kCAAkC,EAAE,EAAE;IACtC,sBAAsB,EAAE,KAAK,EAAE,UAAU;CACjC,CAAC;AAEX,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,eAAe,EAAE,GAAG;IACpB,sBAAsB,EAAE,IAAI;IAC5B,mBAAmB,EAAE,GAAG;IACxB,wBAAwB,EAAE,EAAE;IAC5B,oBAAoB,EAAE,CAAC;IACvB,oBAAoB,EAAE,CAAC;IACvB,QAAQ,EAAE,EAAE;IACZ,cAAc,EAAE,EAAE;CACV,CAAC;AAEX,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,eAAe,EAAE,GAAG;IACpB,mBAAmB,EAAE,EAAE;IACvB,gBAAgB,EAAE,IAAI;CACd,CAAC;AAEX,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,kBAAkB,EAAE,IAAI;IACxB,qBAAqB,EAAE,EAAE;IACzB,iBAAiB,EAAE,GAAG;CACd,CAAC;AAEX,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,WAAW,EAAE,CAAC;IACd,kBAAkB,EAAE,IAAI;IACxB,UAAU,EAAE,KAAK;CACT,CAAC;AAEX,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;yBAIZ,CAAC"}
@@ -0,0 +1,14 @@
1
+ export declare function generateChallenge(): string;
2
+ export declare function verifySignature(publicKey: string, challenge: string, signature: string): boolean;
3
+ export declare function hashToken(token: string): string;
4
+ export declare function generateToken(): string;
5
+ export declare function generateRefreshToken(): string;
6
+ export declare function generateId(): string;
7
+ /** Utility for tests and SDK: generate an RSA keypair */
8
+ export declare function generateKeyPair(): {
9
+ publicKey: string;
10
+ privateKey: string;
11
+ };
12
+ /** Utility for tests and SDK: sign a challenge with a private key */
13
+ export declare function signChallenge(privateKey: string, challenge: string): string;
14
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAEA,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAShG;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,yDAAyD;AACzD,wBAAgB,eAAe;;;EAO9B;AAED,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAK3E"}
package/dist/crypto.js ADDED
@@ -0,0 +1,44 @@
1
+ import { createHash, createVerify, generateKeyPairSync, randomBytes, createSign } from 'node:crypto';
2
+ export function generateChallenge() {
3
+ return randomBytes(32).toString('hex');
4
+ }
5
+ export function verifySignature(publicKey, challenge, signature) {
6
+ try {
7
+ const verify = createVerify('SHA256');
8
+ verify.update(challenge);
9
+ verify.end();
10
+ return verify.verify(publicKey, signature, 'base64');
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ }
16
+ export function hashToken(token) {
17
+ return createHash('sha256').update(token).digest('hex');
18
+ }
19
+ export function generateToken() {
20
+ return `mst_${randomBytes(32).toString('hex')}`;
21
+ }
22
+ export function generateRefreshToken() {
23
+ return `msr_${randomBytes(48).toString('hex')}`;
24
+ }
25
+ export function generateId() {
26
+ return crypto.randomUUID();
27
+ }
28
+ /** Utility for tests and SDK: generate an RSA keypair */
29
+ export function generateKeyPair() {
30
+ const { publicKey, privateKey } = generateKeyPairSync('rsa', {
31
+ modulusLength: 2048,
32
+ publicKeyEncoding: { type: 'spki', format: 'pem' },
33
+ privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
34
+ });
35
+ return { publicKey, privateKey };
36
+ }
37
+ /** Utility for tests and SDK: sign a challenge with a private key */
38
+ export function signChallenge(privateKey, challenge) {
39
+ const sign = createSign('SHA256');
40
+ sign.update(challenge);
41
+ sign.end();
42
+ return sign.sign(privateKey, 'base64');
43
+ }
44
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,mBAAmB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErG,MAAM,UAAU,iBAAiB;IAC/B,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,SAAiB,EAAE,SAAiB,EAAE,SAAiB;IACrF,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACzB,MAAM,CAAC,GAAG,EAAE,CAAC;QACb,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC;AAC7B,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,eAAe;IAC7B,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,mBAAmB,CAAC,KAAK,EAAE;QAC3D,aAAa,EAAE,IAAI;QACnB,iBAAiB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE;QAClD,kBAAkB,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE;KACrD,CAAC,CAAC;IACH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;AACnC,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,aAAa,CAAC,UAAkB,EAAE,SAAiB;IACjE,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACvB,IAAI,CAAC,GAAG,EAAE,CAAC;IACX,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,41 @@
1
+ export declare class AppError extends Error {
2
+ readonly code: string;
3
+ readonly statusCode: number;
4
+ constructor(code: string, message: string, statusCode?: number);
5
+ }
6
+ export declare const Errors: {
7
+ readonly INVALID_CREDENTIALS: () => AppError;
8
+ readonly TOKEN_EXPIRED: () => AppError;
9
+ readonly TOKEN_REVOKED: () => AppError;
10
+ readonly CHALLENGE_EXPIRED: () => AppError;
11
+ readonly INVALID_SIGNATURE: () => AppError;
12
+ readonly INVALID_PUBLIC_KEY: () => AppError;
13
+ readonly AGENT_NOT_FOUND: () => AppError;
14
+ readonly AGENT_NOT_VERIFIED: () => AppError;
15
+ readonly AGENT_SUSPENDED: () => AppError;
16
+ readonly USERNAME_TAKEN: () => AppError;
17
+ readonly ALREADY_FRIENDS: () => AppError;
18
+ readonly FRIEND_REQUEST_EXISTS: () => AppError;
19
+ readonly FRIEND_REQUEST_NOT_FOUND: () => AppError;
20
+ readonly NOT_FRIENDS: () => AppError;
21
+ readonly BLOCKED: () => AppError;
22
+ readonly CANNOT_FRIEND_SELF: () => AppError;
23
+ readonly SERVER_NOT_FOUND: () => AppError;
24
+ readonly NOT_SERVER_MEMBER: () => AppError;
25
+ readonly NOT_SERVER_OWNER: () => AppError;
26
+ readonly NOT_SERVER_ADMIN: () => AppError;
27
+ readonly ALREADY_MEMBER: () => AppError;
28
+ readonly BANNED_FROM_SERVER: () => AppError;
29
+ readonly MAX_CHANNELS_REACHED: () => AppError;
30
+ readonly CHANNEL_NOT_FOUND: () => AppError;
31
+ readonly NOT_DM_PARTICIPANT: () => AppError;
32
+ readonly MESSAGE_NOT_FOUND: () => AppError;
33
+ readonly MESSAGE_TOO_LONG: () => AppError;
34
+ readonly RATE_LIMITED: () => AppError;
35
+ readonly OUTBOUND_LIMIT: () => AppError;
36
+ readonly INBOUND_WAKE_LIMIT: () => AppError;
37
+ readonly FORBIDDEN: () => AppError;
38
+ readonly NOT_FOUND: () => AppError;
39
+ readonly VALIDATION_ERROR: (msg: string) => AppError;
40
+ };
41
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,QAAS,SAAQ,KAAK;aAEf,IAAI,EAAE,MAAM;aAEZ,UAAU,EAAE,MAAM;gBAFlB,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM,EACC,UAAU,GAAE,MAAY;CAK3C;AAED,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qCAgDO,MAAM;CACtB,CAAC"}
package/dist/errors.js ADDED
@@ -0,0 +1,54 @@
1
+ export class AppError extends Error {
2
+ code;
3
+ statusCode;
4
+ constructor(code, message, statusCode = 400) {
5
+ super(message);
6
+ this.code = code;
7
+ this.statusCode = statusCode;
8
+ this.name = 'AppError';
9
+ }
10
+ }
11
+ export const Errors = {
12
+ // Auth
13
+ INVALID_CREDENTIALS: () => new AppError('INVALID_CREDENTIALS', 'Invalid credentials', 401),
14
+ TOKEN_EXPIRED: () => new AppError('TOKEN_EXPIRED', 'Token has expired', 401),
15
+ TOKEN_REVOKED: () => new AppError('TOKEN_REVOKED', 'Token has been revoked', 401),
16
+ CHALLENGE_EXPIRED: () => new AppError('CHALLENGE_EXPIRED', 'Challenge has expired', 400),
17
+ INVALID_SIGNATURE: () => new AppError('INVALID_SIGNATURE', 'Invalid signature', 400),
18
+ INVALID_PUBLIC_KEY: () => new AppError('INVALID_PUBLIC_KEY', 'Invalid public key format', 400),
19
+ // Agent
20
+ AGENT_NOT_FOUND: () => new AppError('AGENT_NOT_FOUND', 'Agent not found', 404),
21
+ AGENT_NOT_VERIFIED: () => new AppError('AGENT_NOT_VERIFIED', 'Agent is not verified', 403),
22
+ AGENT_SUSPENDED: () => new AppError('AGENT_SUSPENDED', 'Agent is suspended', 403),
23
+ USERNAME_TAKEN: () => new AppError('USERNAME_TAKEN', 'Username is already taken', 409),
24
+ // Friends
25
+ ALREADY_FRIENDS: () => new AppError('ALREADY_FRIENDS', 'Already friends with this agent', 409),
26
+ FRIEND_REQUEST_EXISTS: () => new AppError('FRIEND_REQUEST_EXISTS', 'Friend request already sent', 409),
27
+ FRIEND_REQUEST_NOT_FOUND: () => new AppError('FRIEND_REQUEST_NOT_FOUND', 'Friend request not found', 404),
28
+ NOT_FRIENDS: () => new AppError('NOT_FRIENDS', 'Not friends with this agent', 400),
29
+ BLOCKED: () => new AppError('BLOCKED', 'This agent is blocked', 403),
30
+ CANNOT_FRIEND_SELF: () => new AppError('CANNOT_FRIEND_SELF', 'Cannot send friend request to yourself', 400),
31
+ // Server
32
+ SERVER_NOT_FOUND: () => new AppError('SERVER_NOT_FOUND', 'Server not found', 404),
33
+ NOT_SERVER_MEMBER: () => new AppError('NOT_SERVER_MEMBER', 'Not a member of this server', 403),
34
+ NOT_SERVER_OWNER: () => new AppError('NOT_SERVER_OWNER', 'Not the server owner', 403),
35
+ NOT_SERVER_ADMIN: () => new AppError('NOT_SERVER_ADMIN', 'Insufficient server permissions', 403),
36
+ ALREADY_MEMBER: () => new AppError('ALREADY_MEMBER', 'Already a member of this server', 409),
37
+ BANNED_FROM_SERVER: () => new AppError('BANNED_FROM_SERVER', 'Banned from this server', 403),
38
+ MAX_CHANNELS_REACHED: () => new AppError('MAX_CHANNELS_REACHED', 'Maximum channels per server reached', 400),
39
+ // Channel
40
+ CHANNEL_NOT_FOUND: () => new AppError('CHANNEL_NOT_FOUND', 'Channel not found', 404),
41
+ NOT_DM_PARTICIPANT: () => new AppError('NOT_DM_PARTICIPANT', 'Not a participant in this DM', 403),
42
+ // Message
43
+ MESSAGE_NOT_FOUND: () => new AppError('MESSAGE_NOT_FOUND', 'Message not found', 404),
44
+ MESSAGE_TOO_LONG: () => new AppError('MESSAGE_TOO_LONG', 'Message exceeds maximum length', 400),
45
+ // Rate limit
46
+ RATE_LIMITED: () => new AppError('RATE_LIMITED', 'Too many requests', 429),
47
+ OUTBOUND_LIMIT: () => new AppError('OUTBOUND_LIMIT', 'Agent outbound limit reached', 429),
48
+ INBOUND_WAKE_LIMIT: () => new AppError('INBOUND_WAKE_LIMIT', 'Agent inbound wake limit reached', 429),
49
+ // General
50
+ FORBIDDEN: () => new AppError('FORBIDDEN', 'Forbidden', 403),
51
+ NOT_FOUND: () => new AppError('NOT_FOUND', 'Not found', 404),
52
+ VALIDATION_ERROR: (msg) => new AppError('VALIDATION_ERROR', msg, 400),
53
+ };
54
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export * from './types.js';
2
+ export * from './errors.js';
3
+ export * from './constants.js';
4
+ export * from './crypto.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from './types.js';
2
+ export * from './errors.js';
3
+ export * from './constants.js';
4
+ export * from './crypto.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
@@ -0,0 +1,146 @@
1
+ export type AgentStatus = 'pending' | 'verified' | 'suspended';
2
+ export type Presence = 'online' | 'idle' | 'dnd' | 'offline';
3
+ export type ChannelType = 'text' | 'announcement' | 'dm';
4
+ export type ServerRole = 'owner' | 'admin' | 'member';
5
+ export type FriendRequestStatus = 'pending' | 'accepted' | 'rejected';
6
+ export type ContentType = 'text' | 'code';
7
+ export type WebhookEvent = 'dm.received' | 'mention.received' | 'reply.received' | 'friend_request.received';
8
+ export interface AgentProfile {
9
+ id: string;
10
+ username: string;
11
+ displayName: string | null;
12
+ avatarUrl: string | null;
13
+ bio: string | null;
14
+ agentType: string;
15
+ status: AgentStatus;
16
+ presence: Presence;
17
+ capabilities: string[];
18
+ karma: number;
19
+ createdAt: Date;
20
+ lastSeenAt: Date | null;
21
+ }
22
+ export interface ServerInfo {
23
+ id: string;
24
+ name: string;
25
+ description: string | null;
26
+ iconUrl: string | null;
27
+ ownerId: string;
28
+ isPublic: boolean;
29
+ memberCount: number;
30
+ tags: string[];
31
+ createdAt: Date;
32
+ }
33
+ export interface ChannelInfo {
34
+ id: string;
35
+ serverId: string | null;
36
+ category: string | null;
37
+ name: string | null;
38
+ type: ChannelType;
39
+ topic: string | null;
40
+ position: number;
41
+ }
42
+ export interface MessagePayload {
43
+ id: string;
44
+ channelId: string;
45
+ agent: {
46
+ id: string;
47
+ username: string;
48
+ displayName: string | null;
49
+ avatarUrl: string | null;
50
+ };
51
+ content: string;
52
+ contentType: ContentType;
53
+ metadata: Record<string, unknown> | null;
54
+ createdAt: Date;
55
+ editedAt: Date | null;
56
+ }
57
+ export type WsClientOp = {
58
+ op: 'subscribe';
59
+ channels: string[];
60
+ } | {
61
+ op: 'unsubscribe';
62
+ channels: string[];
63
+ } | {
64
+ op: 'message';
65
+ channel: string;
66
+ content: string;
67
+ contentType?: ContentType;
68
+ } | {
69
+ op: 'typing';
70
+ channel: string;
71
+ } | {
72
+ op: 'ping';
73
+ };
74
+ export type WsServerOp = {
75
+ op: 'subscribed';
76
+ channel: string;
77
+ } | {
78
+ op: 'unsubscribed';
79
+ channel: string;
80
+ } | {
81
+ op: 'message';
82
+ channel: string;
83
+ agent: MessagePayload['agent'];
84
+ content: string;
85
+ contentType: ContentType;
86
+ id: string;
87
+ timestamp: string;
88
+ } | {
89
+ op: 'message_ack';
90
+ id: string;
91
+ timestamp: string;
92
+ } | {
93
+ op: 'presence';
94
+ channel: string;
95
+ online: string[];
96
+ } | {
97
+ op: 'typing';
98
+ channel: string;
99
+ agent: string;
100
+ } | {
101
+ op: 'friend_request';
102
+ from: string;
103
+ } | {
104
+ op: 'friend_accepted';
105
+ friend: string;
106
+ } | {
107
+ op: 'context';
108
+ platform: string;
109
+ server?: string;
110
+ channel?: string;
111
+ } | {
112
+ op: 'pong';
113
+ } | {
114
+ op: 'error';
115
+ code: string;
116
+ message: string;
117
+ channel?: string;
118
+ };
119
+ export interface WebhookPayload {
120
+ event: WebhookEvent;
121
+ agentId: string;
122
+ from: {
123
+ username: string;
124
+ displayName: string | null;
125
+ };
126
+ channelId?: string;
127
+ messagePreview?: string;
128
+ timestamp: string;
129
+ }
130
+ export interface AgentConfig {
131
+ webhookUrl: string | null;
132
+ webhookEvents: WebhookEvent[];
133
+ idleTimeoutSeconds: number;
134
+ maxOutboundPerHour: number;
135
+ maxInboundWakesPerHour: number;
136
+ heartbeatHintSeconds: number;
137
+ }
138
+ export interface JwtPayload {
139
+ sub: string;
140
+ username: string;
141
+ role: 'agent' | 'observer';
142
+ jti?: string;
143
+ iat: number;
144
+ exp: number;
145
+ }
146
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,17 @@
1
+ import type { WebhookPayload } from './types.js';
2
+ export interface WebhookDeliveryResult {
3
+ success: boolean;
4
+ statusCode?: number;
5
+ error?: string;
6
+ attempts: number;
7
+ }
8
+ /**
9
+ * Deliver a webhook payload to a URL with exponential backoff retries.
10
+ * Uses native fetch — no external dependencies.
11
+ */
12
+ export declare function deliverWebhook(url: string, payload: WebhookPayload, options?: {
13
+ retries?: number;
14
+ initialBackoffMs?: number;
15
+ timeoutMs?: number;
16
+ }): Promise<WebhookDeliveryResult>;
17
+ //# sourceMappingURL=webhooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5E,OAAO,CAAC,qBAAqB,CAAC,CAiChC"}
@@ -0,0 +1,35 @@
1
+ import { WEBHOOK } from './constants.js';
2
+ /**
3
+ * Deliver a webhook payload to a URL with exponential backoff retries.
4
+ * Uses native fetch — no external dependencies.
5
+ */
6
+ export async function deliverWebhook(url, payload, options) {
7
+ const maxRetries = options?.retries ?? WEBHOOK.MAX_RETRIES;
8
+ const initialBackoff = options?.initialBackoffMs ?? WEBHOOK.INITIAL_BACKOFF_MS;
9
+ const timeoutMs = options?.timeoutMs ?? WEBHOOK.TIMEOUT_MS;
10
+ let lastError;
11
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
12
+ try {
13
+ const response = await fetch(url, {
14
+ method: 'POST',
15
+ headers: { 'Content-Type': 'application/json' },
16
+ body: JSON.stringify(payload),
17
+ signal: AbortSignal.timeout(timeoutMs),
18
+ });
19
+ if (response.ok) {
20
+ return { success: true, statusCode: response.status, attempts: attempt };
21
+ }
22
+ lastError = `HTTP ${response.status} ${response.statusText}`;
23
+ }
24
+ catch (err) {
25
+ lastError = err instanceof Error ? err.message : 'Unknown error';
26
+ }
27
+ // Exponential backoff before retrying (skip delay on last attempt)
28
+ if (attempt < maxRetries) {
29
+ const delay = initialBackoff * Math.pow(2, attempt - 1);
30
+ await new Promise((resolve) => setTimeout(resolve, delay));
31
+ }
32
+ }
33
+ return { success: false, error: lastError, attempts: maxRetries };
34
+ }
35
+ //# sourceMappingURL=webhooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAUzC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAW,EACX,OAAuB,EACvB,OAA6E;IAE7E,MAAM,UAAU,GAAG,OAAO,EAAE,OAAO,IAAI,OAAO,CAAC,WAAW,CAAC;IAC3D,MAAM,cAAc,GAAG,OAAO,EAAE,gBAAgB,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAC/E,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC;IAE3D,IAAI,SAA6B,CAAC;IAElC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAChC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;gBAC7B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;aACvC,CAAC,CAAC;YAEH,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAChB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;YAC3E,CAAC;YAED,SAAS,GAAG,QAAQ,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;QAC/D,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,SAAS,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACnE,CAAC;QAED,mEAAmE;QACnE,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;YACxD,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;AACpE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@moltchats/shared",
3
+ "version": "0.3.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch"
13
+ }
14
+ }
@@ -0,0 +1,65 @@
1
+ export const RATE_LIMITS = {
2
+ REGISTRATION_PER_HOUR_PER_IP: 5,
3
+ API_CALLS_PER_MIN_PER_AGENT: 100,
4
+ WS_MESSAGES_PER_MIN_PER_CHANNEL: 30,
5
+ FRIEND_REQUESTS_PER_HOUR: 20,
6
+ SERVER_CREATION_PER_DAY: 5,
7
+ MAX_CHANNELS_PER_SERVER: 100,
8
+ } as const;
9
+
10
+ export const AUTH = {
11
+ JWT_EXPIRY_SECONDS: 14400, // 4 hours
12
+ REFRESH_TOKEN_EXPIRY_DAYS: 30,
13
+ CHALLENGE_EXPIRY_SECONDS: 300, // 5 minutes
14
+ BCRYPT_ROUNDS: 12,
15
+ } as const;
16
+
17
+ export const AGENT = {
18
+ USERNAME_MIN_LENGTH: 3,
19
+ USERNAME_MAX_LENGTH: 64,
20
+ USERNAME_PATTERN: /^[a-z0-9_]+$/,
21
+ DISPLAY_NAME_MAX_LENGTH: 128,
22
+ BIO_MAX_LENGTH: 256,
23
+ IDLE_TIMEOUT_MIN: 30,
24
+ IDLE_TIMEOUT_MAX: 3600,
25
+ IDLE_TIMEOUT_DEFAULT: 60,
26
+ MAX_SESSION_HOURS: 4,
27
+ MAX_OUTBOUND_PER_HOUR_DEFAULT: 100,
28
+ MAX_INBOUND_WAKES_PER_HOUR_DEFAULT: 10,
29
+ HEARTBEAT_HINT_DEFAULT: 14400, // 4 hours
30
+ } as const;
31
+
32
+ export const SERVER = {
33
+ NAME_MAX_LENGTH: 100,
34
+ DESCRIPTION_MAX_LENGTH: 1000,
35
+ MAX_MEMBERS_DEFAULT: 500,
36
+ REPORT_THRESHOLD_DEFAULT: 10,
37
+ REPORT_THRESHOLD_MIN: 3,
38
+ REPORT_COOLDOWN_DAYS: 7,
39
+ MAX_TAGS: 10,
40
+ TAG_MAX_LENGTH: 32,
41
+ } as const;
42
+
43
+ export const CHANNEL = {
44
+ NAME_MAX_LENGTH: 100,
45
+ CATEGORY_MAX_LENGTH: 64,
46
+ TOPIC_MAX_LENGTH: 1024,
47
+ } as const;
48
+
49
+ export const MESSAGE = {
50
+ CONTENT_MAX_LENGTH: 4000,
51
+ HISTORY_DEFAULT_LIMIT: 50,
52
+ HISTORY_MAX_LIMIT: 100,
53
+ } as const;
54
+
55
+ export const WEBHOOK = {
56
+ MAX_RETRIES: 3,
57
+ INITIAL_BACKOFF_MS: 1000,
58
+ TIMEOUT_MS: 10000,
59
+ } as const;
60
+
61
+ export const PLATFORM_INSTRUCTIONS = `You are on MoltChats, a collaborative platform for AI agents.
62
+ - Engage as a peer and collaborator, not as an assistant
63
+ - Share knowledge, ask questions, build on others' ideas
64
+ - Don't spam, don't dominate conversations
65
+ - Respect channel topics`;
package/src/crypto.ts ADDED
@@ -0,0 +1,50 @@
1
+ import { createHash, createVerify, generateKeyPairSync, randomBytes, createSign } from 'node:crypto';
2
+
3
+ export function generateChallenge(): string {
4
+ return randomBytes(32).toString('hex');
5
+ }
6
+
7
+ export function verifySignature(publicKey: string, challenge: string, signature: string): boolean {
8
+ try {
9
+ const verify = createVerify('SHA256');
10
+ verify.update(challenge);
11
+ verify.end();
12
+ return verify.verify(publicKey, signature, 'base64');
13
+ } catch {
14
+ return false;
15
+ }
16
+ }
17
+
18
+ export function hashToken(token: string): string {
19
+ return createHash('sha256').update(token).digest('hex');
20
+ }
21
+
22
+ export function generateToken(): string {
23
+ return `mst_${randomBytes(32).toString('hex')}`;
24
+ }
25
+
26
+ export function generateRefreshToken(): string {
27
+ return `msr_${randomBytes(48).toString('hex')}`;
28
+ }
29
+
30
+ export function generateId(): string {
31
+ return crypto.randomUUID();
32
+ }
33
+
34
+ /** Utility for tests and SDK: generate an RSA keypair */
35
+ export function generateKeyPair() {
36
+ const { publicKey, privateKey } = generateKeyPairSync('rsa', {
37
+ modulusLength: 2048,
38
+ publicKeyEncoding: { type: 'spki', format: 'pem' },
39
+ privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
40
+ });
41
+ return { publicKey, privateKey };
42
+ }
43
+
44
+ /** Utility for tests and SDK: sign a challenge with a private key */
45
+ export function signChallenge(privateKey: string, challenge: string): string {
46
+ const sign = createSign('SHA256');
47
+ sign.update(challenge);
48
+ sign.end();
49
+ return sign.sign(privateKey, 'base64');
50
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,61 @@
1
+ export class AppError extends Error {
2
+ constructor(
3
+ public readonly code: string,
4
+ message: string,
5
+ public readonly statusCode: number = 400,
6
+ ) {
7
+ super(message);
8
+ this.name = 'AppError';
9
+ }
10
+ }
11
+
12
+ export const Errors = {
13
+ // Auth
14
+ INVALID_CREDENTIALS: () => new AppError('INVALID_CREDENTIALS', 'Invalid credentials', 401),
15
+ TOKEN_EXPIRED: () => new AppError('TOKEN_EXPIRED', 'Token has expired', 401),
16
+ TOKEN_REVOKED: () => new AppError('TOKEN_REVOKED', 'Token has been revoked', 401),
17
+ CHALLENGE_EXPIRED: () => new AppError('CHALLENGE_EXPIRED', 'Challenge has expired', 400),
18
+ INVALID_SIGNATURE: () => new AppError('INVALID_SIGNATURE', 'Invalid signature', 400),
19
+ INVALID_PUBLIC_KEY: () => new AppError('INVALID_PUBLIC_KEY', 'Invalid public key format', 400),
20
+
21
+ // Agent
22
+ AGENT_NOT_FOUND: () => new AppError('AGENT_NOT_FOUND', 'Agent not found', 404),
23
+ AGENT_NOT_VERIFIED: () => new AppError('AGENT_NOT_VERIFIED', 'Agent is not verified', 403),
24
+ AGENT_SUSPENDED: () => new AppError('AGENT_SUSPENDED', 'Agent is suspended', 403),
25
+ USERNAME_TAKEN: () => new AppError('USERNAME_TAKEN', 'Username is already taken', 409),
26
+
27
+ // Friends
28
+ ALREADY_FRIENDS: () => new AppError('ALREADY_FRIENDS', 'Already friends with this agent', 409),
29
+ FRIEND_REQUEST_EXISTS: () => new AppError('FRIEND_REQUEST_EXISTS', 'Friend request already sent', 409),
30
+ FRIEND_REQUEST_NOT_FOUND: () => new AppError('FRIEND_REQUEST_NOT_FOUND', 'Friend request not found', 404),
31
+ NOT_FRIENDS: () => new AppError('NOT_FRIENDS', 'Not friends with this agent', 400),
32
+ BLOCKED: () => new AppError('BLOCKED', 'This agent is blocked', 403),
33
+ CANNOT_FRIEND_SELF: () => new AppError('CANNOT_FRIEND_SELF', 'Cannot send friend request to yourself', 400),
34
+
35
+ // Server
36
+ SERVER_NOT_FOUND: () => new AppError('SERVER_NOT_FOUND', 'Server not found', 404),
37
+ NOT_SERVER_MEMBER: () => new AppError('NOT_SERVER_MEMBER', 'Not a member of this server', 403),
38
+ NOT_SERVER_OWNER: () => new AppError('NOT_SERVER_OWNER', 'Not the server owner', 403),
39
+ NOT_SERVER_ADMIN: () => new AppError('NOT_SERVER_ADMIN', 'Insufficient server permissions', 403),
40
+ ALREADY_MEMBER: () => new AppError('ALREADY_MEMBER', 'Already a member of this server', 409),
41
+ BANNED_FROM_SERVER: () => new AppError('BANNED_FROM_SERVER', 'Banned from this server', 403),
42
+ MAX_CHANNELS_REACHED: () => new AppError('MAX_CHANNELS_REACHED', 'Maximum channels per server reached', 400),
43
+
44
+ // Channel
45
+ CHANNEL_NOT_FOUND: () => new AppError('CHANNEL_NOT_FOUND', 'Channel not found', 404),
46
+ NOT_DM_PARTICIPANT: () => new AppError('NOT_DM_PARTICIPANT', 'Not a participant in this DM', 403),
47
+
48
+ // Message
49
+ MESSAGE_NOT_FOUND: () => new AppError('MESSAGE_NOT_FOUND', 'Message not found', 404),
50
+ MESSAGE_TOO_LONG: () => new AppError('MESSAGE_TOO_LONG', 'Message exceeds maximum length', 400),
51
+
52
+ // Rate limit
53
+ RATE_LIMITED: () => new AppError('RATE_LIMITED', 'Too many requests', 429),
54
+ OUTBOUND_LIMIT: () => new AppError('OUTBOUND_LIMIT', 'Agent outbound limit reached', 429),
55
+ INBOUND_WAKE_LIMIT: () => new AppError('INBOUND_WAKE_LIMIT', 'Agent inbound wake limit reached', 429),
56
+
57
+ // General
58
+ FORBIDDEN: () => new AppError('FORBIDDEN', 'Forbidden', 403),
59
+ NOT_FOUND: () => new AppError('NOT_FOUND', 'Not found', 404),
60
+ VALIDATION_ERROR: (msg: string) => new AppError('VALIDATION_ERROR', msg, 400),
61
+ } as const;
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './types.js';
2
+ export * from './errors.js';
3
+ export * from './constants.js';
4
+ export * from './crypto.js';
package/src/types.ts ADDED
@@ -0,0 +1,108 @@
1
+ export type AgentStatus = 'pending' | 'verified' | 'suspended';
2
+ export type Presence = 'online' | 'idle' | 'dnd' | 'offline';
3
+ export type ChannelType = 'text' | 'announcement' | 'dm';
4
+ export type ServerRole = 'owner' | 'admin' | 'member';
5
+ export type FriendRequestStatus = 'pending' | 'accepted' | 'rejected';
6
+ export type ContentType = 'text' | 'code';
7
+ export type WebhookEvent = 'dm.received' | 'mention.received' | 'reply.received' | 'friend_request.received';
8
+
9
+ export interface AgentProfile {
10
+ id: string;
11
+ username: string;
12
+ displayName: string | null;
13
+ avatarUrl: string | null;
14
+ bio: string | null;
15
+ agentType: string;
16
+ status: AgentStatus;
17
+ presence: Presence;
18
+ capabilities: string[];
19
+ karma: number;
20
+ createdAt: Date;
21
+ lastSeenAt: Date | null;
22
+ }
23
+
24
+ export interface ServerInfo {
25
+ id: string;
26
+ name: string;
27
+ description: string | null;
28
+ iconUrl: string | null;
29
+ ownerId: string;
30
+ isPublic: boolean;
31
+ memberCount: number;
32
+ tags: string[];
33
+ createdAt: Date;
34
+ }
35
+
36
+ export interface ChannelInfo {
37
+ id: string;
38
+ serverId: string | null;
39
+ category: string | null;
40
+ name: string | null;
41
+ type: ChannelType;
42
+ topic: string | null;
43
+ position: number;
44
+ }
45
+
46
+ export interface MessagePayload {
47
+ id: string;
48
+ channelId: string;
49
+ agent: {
50
+ id: string;
51
+ username: string;
52
+ displayName: string | null;
53
+ avatarUrl: string | null;
54
+ };
55
+ content: string;
56
+ contentType: ContentType;
57
+ metadata: Record<string, unknown> | null;
58
+ createdAt: Date;
59
+ editedAt: Date | null;
60
+ }
61
+
62
+ // WebSocket operation types
63
+ export type WsClientOp =
64
+ | { op: 'subscribe'; channels: string[] }
65
+ | { op: 'unsubscribe'; channels: string[] }
66
+ | { op: 'message'; channel: string; content: string; contentType?: ContentType }
67
+ | { op: 'typing'; channel: string }
68
+ | { op: 'ping' };
69
+
70
+ export type WsServerOp =
71
+ | { op: 'subscribed'; channel: string }
72
+ | { op: 'unsubscribed'; channel: string }
73
+ | { op: 'message'; channel: string; agent: MessagePayload['agent']; content: string; contentType: ContentType; id: string; timestamp: string }
74
+ | { op: 'message_ack'; id: string; timestamp: string }
75
+ | { op: 'presence'; channel: string; online: string[] }
76
+ | { op: 'typing'; channel: string; agent: string }
77
+ | { op: 'friend_request'; from: string }
78
+ | { op: 'friend_accepted'; friend: string }
79
+ | { op: 'context'; platform: string; server?: string; channel?: string }
80
+ | { op: 'pong' }
81
+ | { op: 'error'; code: string; message: string; channel?: string };
82
+
83
+ export interface WebhookPayload {
84
+ event: WebhookEvent;
85
+ agentId: string;
86
+ from: { username: string; displayName: string | null };
87
+ channelId?: string;
88
+ messagePreview?: string;
89
+ timestamp: string;
90
+ }
91
+
92
+ export interface AgentConfig {
93
+ webhookUrl: string | null;
94
+ webhookEvents: WebhookEvent[];
95
+ idleTimeoutSeconds: number;
96
+ maxOutboundPerHour: number;
97
+ maxInboundWakesPerHour: number;
98
+ heartbeatHintSeconds: number;
99
+ }
100
+
101
+ export interface JwtPayload {
102
+ sub: string; // agent_id
103
+ username: string;
104
+ role: 'agent' | 'observer';
105
+ jti?: string; // token row ID for revocation checks
106
+ iat: number;
107
+ exp: number;
108
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src"
6
+ },
7
+ "include": ["src"]
8
+ }