@herdctl/slack 0.0.1 → 0.2.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 (102) hide show
  1. package/LICENSE +21 -0
  2. package/dist/__tests__/command-handler.test.d.ts +2 -0
  3. package/dist/__tests__/command-handler.test.d.ts.map +1 -0
  4. package/dist/__tests__/command-handler.test.js +141 -0
  5. package/dist/__tests__/command-handler.test.js.map +1 -0
  6. package/dist/__tests__/error-handler.test.d.ts +2 -0
  7. package/dist/__tests__/error-handler.test.d.ts.map +1 -0
  8. package/dist/__tests__/error-handler.test.js +168 -0
  9. package/dist/__tests__/error-handler.test.js.map +1 -0
  10. package/dist/__tests__/errors.test.d.ts +2 -0
  11. package/dist/__tests__/errors.test.d.ts.map +1 -0
  12. package/dist/__tests__/errors.test.js +133 -0
  13. package/dist/__tests__/errors.test.js.map +1 -0
  14. package/dist/__tests__/formatting.test.d.ts +2 -0
  15. package/dist/__tests__/formatting.test.d.ts.map +1 -0
  16. package/dist/__tests__/formatting.test.js +216 -0
  17. package/dist/__tests__/formatting.test.js.map +1 -0
  18. package/dist/__tests__/logger.test.d.ts +2 -0
  19. package/dist/__tests__/logger.test.d.ts.map +1 -0
  20. package/dist/__tests__/logger.test.js +97 -0
  21. package/dist/__tests__/logger.test.js.map +1 -0
  22. package/dist/__tests__/message-handler.test.d.ts +2 -0
  23. package/dist/__tests__/message-handler.test.d.ts.map +1 -0
  24. package/dist/__tests__/message-handler.test.js +78 -0
  25. package/dist/__tests__/message-handler.test.js.map +1 -0
  26. package/dist/__tests__/session-manager.test.d.ts +2 -0
  27. package/dist/__tests__/session-manager.test.d.ts.map +1 -0
  28. package/dist/__tests__/session-manager.test.js +214 -0
  29. package/dist/__tests__/session-manager.test.js.map +1 -0
  30. package/dist/__tests__/slack-connector.test.d.ts +2 -0
  31. package/dist/__tests__/slack-connector.test.d.ts.map +1 -0
  32. package/dist/__tests__/slack-connector.test.js +376 -0
  33. package/dist/__tests__/slack-connector.test.js.map +1 -0
  34. package/dist/commands/command-handler.d.ts +72 -0
  35. package/dist/commands/command-handler.d.ts.map +1 -0
  36. package/dist/commands/command-handler.js +85 -0
  37. package/dist/commands/command-handler.js.map +1 -0
  38. package/dist/commands/help.d.ts +6 -0
  39. package/dist/commands/help.d.ts.map +1 -0
  40. package/dist/commands/help.js +22 -0
  41. package/dist/commands/help.js.map +1 -0
  42. package/dist/commands/index.d.ts +11 -0
  43. package/dist/commands/index.d.ts.map +1 -0
  44. package/dist/commands/index.js +10 -0
  45. package/dist/commands/index.js.map +1 -0
  46. package/dist/commands/reset.d.ts +6 -0
  47. package/dist/commands/reset.d.ts.map +1 -0
  48. package/dist/commands/reset.js +18 -0
  49. package/dist/commands/reset.js.map +1 -0
  50. package/dist/commands/status.d.ts +6 -0
  51. package/dist/commands/status.d.ts.map +1 -0
  52. package/dist/commands/status.js +94 -0
  53. package/dist/commands/status.js.map +1 -0
  54. package/dist/error-handler.d.ts +50 -0
  55. package/dist/error-handler.d.ts.map +1 -0
  56. package/dist/error-handler.js +123 -0
  57. package/dist/error-handler.js.map +1 -0
  58. package/dist/errors.d.ts +59 -0
  59. package/dist/errors.d.ts.map +1 -0
  60. package/dist/errors.js +73 -0
  61. package/dist/errors.js.map +1 -0
  62. package/dist/formatting.d.ts +83 -0
  63. package/dist/formatting.d.ts.map +1 -0
  64. package/dist/formatting.js +155 -0
  65. package/dist/formatting.js.map +1 -0
  66. package/dist/index.d.ts +31 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +34 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/logger.d.ts +28 -0
  71. package/dist/logger.d.ts.map +1 -0
  72. package/dist/logger.js +41 -0
  73. package/dist/logger.js.map +1 -0
  74. package/dist/message-handler.d.ts +62 -0
  75. package/dist/message-handler.d.ts.map +1 -0
  76. package/dist/message-handler.js +85 -0
  77. package/dist/message-handler.js.map +1 -0
  78. package/dist/session-manager/errors.d.ts +58 -0
  79. package/dist/session-manager/errors.d.ts.map +1 -0
  80. package/dist/session-manager/errors.js +70 -0
  81. package/dist/session-manager/errors.js.map +1 -0
  82. package/dist/session-manager/index.d.ts +9 -0
  83. package/dist/session-manager/index.d.ts.map +1 -0
  84. package/dist/session-manager/index.js +13 -0
  85. package/dist/session-manager/index.js.map +1 -0
  86. package/dist/session-manager/session-manager.d.ts +62 -0
  87. package/dist/session-manager/session-manager.d.ts.map +1 -0
  88. package/dist/session-manager/session-manager.js +320 -0
  89. package/dist/session-manager/session-manager.js.map +1 -0
  90. package/dist/session-manager/types.d.ts +154 -0
  91. package/dist/session-manager/types.d.ts.map +1 -0
  92. package/dist/session-manager/types.js +57 -0
  93. package/dist/session-manager/types.js.map +1 -0
  94. package/dist/slack-connector.d.ts +53 -0
  95. package/dist/slack-connector.d.ts.map +1 -0
  96. package/dist/slack-connector.js +447 -0
  97. package/dist/slack-connector.js.map +1 -0
  98. package/dist/types.d.ts +211 -0
  99. package/dist/types.d.ts.map +1 -0
  100. package/dist/types.js +8 -0
  101. package/dist/types.js.map +1 -0
  102. package/package.json +42 -4
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Error classes for Slack session management
3
+ *
4
+ * Provides typed errors for session persistence and retrieval failures.
5
+ */
6
+ /**
7
+ * Error codes for session manager operations
8
+ */
9
+ export declare enum SessionErrorCode {
10
+ STATE_READ_FAILED = "SESSION_STATE_READ_FAILED",
11
+ STATE_WRITE_FAILED = "SESSION_STATE_WRITE_FAILED",
12
+ DIRECTORY_CREATE_FAILED = "SESSION_DIRECTORY_CREATE_FAILED",
13
+ SESSION_NOT_FOUND = "SESSION_NOT_FOUND",
14
+ SESSION_EXPIRED = "SESSION_EXPIRED",
15
+ INVALID_STATE = "SESSION_INVALID_STATE"
16
+ }
17
+ /**
18
+ * Base error class for session manager operations
19
+ */
20
+ export declare class SessionManagerError extends Error {
21
+ readonly code: SessionErrorCode;
22
+ readonly agentName: string;
23
+ constructor(message: string, code: SessionErrorCode, agentName: string, options?: {
24
+ cause?: Error;
25
+ });
26
+ }
27
+ /**
28
+ * Error thrown when session state file cannot be read
29
+ */
30
+ export declare class SessionStateReadError extends SessionManagerError {
31
+ readonly path: string;
32
+ constructor(agentName: string, path: string, options?: {
33
+ cause?: Error;
34
+ });
35
+ }
36
+ /**
37
+ * Error thrown when session state file cannot be written
38
+ */
39
+ export declare class SessionStateWriteError extends SessionManagerError {
40
+ readonly path: string;
41
+ constructor(agentName: string, path: string, options?: {
42
+ cause?: Error;
43
+ });
44
+ }
45
+ /**
46
+ * Error thrown when session directory cannot be created
47
+ */
48
+ export declare class SessionDirectoryCreateError extends SessionManagerError {
49
+ readonly path: string;
50
+ constructor(agentName: string, path: string, options?: {
51
+ cause?: Error;
52
+ });
53
+ }
54
+ /**
55
+ * Type guard to check if an error is a SessionManagerError
56
+ */
57
+ export declare function isSessionManagerError(error: unknown): error is SessionManagerError;
58
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/session-manager/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,oBAAY,gBAAgB;IAC1B,iBAAiB,8BAA8B;IAC/C,kBAAkB,+BAA+B;IACjD,uBAAuB,oCAAoC;IAC3D,iBAAiB,sBAAsB;IACvC,eAAe,oBAAoB;IACnC,aAAa,0BAA0B;CACxC;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,SAAgB,IAAI,EAAE,gBAAgB,CAAC;IACvC,SAAgB,SAAS,EAAE,MAAM,CAAC;gBAGhC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,gBAAgB,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE;CAO9B;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,mBAAmB;IAC5D,SAAgB,IAAI,EAAE,MAAM,CAAC;gBAG3B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE;CAW9B;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,mBAAmB;IAC7D,SAAgB,IAAI,EAAE,MAAM,CAAC;gBAG3B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE;CAW9B;AAED;;GAEG;AACH,qBAAa,2BAA4B,SAAQ,mBAAmB;IAClE,SAAgB,IAAI,EAAE,MAAM,CAAC;gBAG3B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE;CAW9B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,mBAAmB,CAE9B"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Error classes for Slack session management
3
+ *
4
+ * Provides typed errors for session persistence and retrieval failures.
5
+ */
6
+ /**
7
+ * Error codes for session manager operations
8
+ */
9
+ export var SessionErrorCode;
10
+ (function (SessionErrorCode) {
11
+ SessionErrorCode["STATE_READ_FAILED"] = "SESSION_STATE_READ_FAILED";
12
+ SessionErrorCode["STATE_WRITE_FAILED"] = "SESSION_STATE_WRITE_FAILED";
13
+ SessionErrorCode["DIRECTORY_CREATE_FAILED"] = "SESSION_DIRECTORY_CREATE_FAILED";
14
+ SessionErrorCode["SESSION_NOT_FOUND"] = "SESSION_NOT_FOUND";
15
+ SessionErrorCode["SESSION_EXPIRED"] = "SESSION_EXPIRED";
16
+ SessionErrorCode["INVALID_STATE"] = "SESSION_INVALID_STATE";
17
+ })(SessionErrorCode || (SessionErrorCode = {}));
18
+ /**
19
+ * Base error class for session manager operations
20
+ */
21
+ export class SessionManagerError extends Error {
22
+ code;
23
+ agentName;
24
+ constructor(message, code, agentName, options) {
25
+ super(message, options);
26
+ this.name = "SessionManagerError";
27
+ this.code = code;
28
+ this.agentName = agentName;
29
+ }
30
+ }
31
+ /**
32
+ * Error thrown when session state file cannot be read
33
+ */
34
+ export class SessionStateReadError extends SessionManagerError {
35
+ path;
36
+ constructor(agentName, path, options) {
37
+ super(`Failed to read session state for agent '${agentName}' from '${path}'`, SessionErrorCode.STATE_READ_FAILED, agentName, options);
38
+ this.name = "SessionStateReadError";
39
+ this.path = path;
40
+ }
41
+ }
42
+ /**
43
+ * Error thrown when session state file cannot be written
44
+ */
45
+ export class SessionStateWriteError extends SessionManagerError {
46
+ path;
47
+ constructor(agentName, path, options) {
48
+ super(`Failed to write session state for agent '${agentName}' to '${path}'`, SessionErrorCode.STATE_WRITE_FAILED, agentName, options);
49
+ this.name = "SessionStateWriteError";
50
+ this.path = path;
51
+ }
52
+ }
53
+ /**
54
+ * Error thrown when session directory cannot be created
55
+ */
56
+ export class SessionDirectoryCreateError extends SessionManagerError {
57
+ path;
58
+ constructor(agentName, path, options) {
59
+ super(`Failed to create session directory for agent '${agentName}' at '${path}'`, SessionErrorCode.DIRECTORY_CREATE_FAILED, agentName, options);
60
+ this.name = "SessionDirectoryCreateError";
61
+ this.path = path;
62
+ }
63
+ }
64
+ /**
65
+ * Type guard to check if an error is a SessionManagerError
66
+ */
67
+ export function isSessionManagerError(error) {
68
+ return error instanceof SessionManagerError;
69
+ }
70
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/session-manager/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,CAAN,IAAY,gBAOX;AAPD,WAAY,gBAAgB;IAC1B,mEAA+C,CAAA;IAC/C,qEAAiD,CAAA;IACjD,+EAA2D,CAAA;IAC3D,2DAAuC,CAAA;IACvC,uDAAmC,CAAA;IACnC,2DAAuC,CAAA;AACzC,CAAC,EAPW,gBAAgB,KAAhB,gBAAgB,QAO3B;AAED;;GAEG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5B,IAAI,CAAmB;IACvB,SAAS,CAAS;IAElC,YACE,OAAe,EACf,IAAsB,EACtB,SAAiB,EACjB,OAA2B;QAE3B,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,qBAAsB,SAAQ,mBAAmB;IAC5C,IAAI,CAAS;IAE7B,YACE,SAAiB,EACjB,IAAY,EACZ,OAA2B;QAE3B,KAAK,CACH,2CAA2C,SAAS,WAAW,IAAI,GAAG,EACtE,gBAAgB,CAAC,iBAAiB,EAClC,SAAS,EACT,OAAO,CACR,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,sBAAuB,SAAQ,mBAAmB;IAC7C,IAAI,CAAS;IAE7B,YACE,SAAiB,EACjB,IAAY,EACZ,OAA2B;QAE3B,KAAK,CACH,4CAA4C,SAAS,SAAS,IAAI,GAAG,EACrE,gBAAgB,CAAC,kBAAkB,EACnC,SAAS,EACT,OAAO,CACR,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,2BAA4B,SAAQ,mBAAmB;IAClD,IAAI,CAAS;IAE7B,YACE,SAAiB,EACjB,IAAY,EACZ,OAA2B;QAE3B,KAAK,CACH,iDAAiD,SAAS,SAAS,IAAI,GAAG,EAC1E,gBAAgB,CAAC,uBAAuB,EACxC,SAAS,EACT,OAAO,CACR,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,6BAA6B,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAc;IAEd,OAAO,KAAK,YAAY,mBAAmB,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Session manager module for Slack
3
+ *
4
+ * Provides per-channel session management for Claude conversations.
5
+ */
6
+ export { SessionManager } from "./session-manager.js";
7
+ export { ChannelSessionSchema, SlackSessionStateSchema, type ChannelSession, type SlackSessionState, type SessionManagerLogger, type SessionManagerOptions, type SessionResult, type ISessionManager, createInitialSessionState, createChannelSession, } from "./types.js";
8
+ export { SessionErrorCode, SessionManagerError, SessionStateReadError, SessionStateWriteError, SessionDirectoryCreateError, isSessionManagerError, } from "./errors.js";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/session-manager/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAEL,oBAAoB,EACpB,uBAAuB,EAEvB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,eAAe,EAEpB,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Session manager module for Slack
3
+ *
4
+ * Provides per-channel session management for Claude conversations.
5
+ */
6
+ export { SessionManager } from "./session-manager.js";
7
+ export {
8
+ // Schemas
9
+ ChannelSessionSchema, SlackSessionStateSchema,
10
+ // Factory functions
11
+ createInitialSessionState, createChannelSession, } from "./types.js";
12
+ export { SessionErrorCode, SessionManagerError, SessionStateReadError, SessionStateWriteError, SessionDirectoryCreateError, isSessionManagerError, } from "./errors.js";
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/session-manager/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO;AACL,UAAU;AACV,oBAAoB,EACpB,uBAAuB;AAQvB,oBAAoB;AACpB,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Session manager for Slack channel conversations
3
+ *
4
+ * Provides per-channel session management for Claude conversations,
5
+ * enabling conversation context preservation across Slack channels.
6
+ *
7
+ * Sessions are stored at .herdctl/slack-sessions/<agent-name>.yaml
8
+ */
9
+ import { type SessionManagerOptions, type ISessionManager, type SessionResult, type ChannelSession } from "./types.js";
10
+ /**
11
+ * SessionManager manages per-channel Claude sessions for a Slack agent.
12
+ *
13
+ * Each agent has its own SessionManager instance, storing session mappings
14
+ * in a YAML file at .herdctl/slack-sessions/<agent-name>.yaml
15
+ *
16
+ * Sessions are keyed by channelId (matching Discord's approach).
17
+ */
18
+ export declare class SessionManager implements ISessionManager {
19
+ readonly agentName: string;
20
+ private readonly stateDir;
21
+ private readonly sessionExpiryHours;
22
+ private readonly logger;
23
+ private readonly stateFilePath;
24
+ private state;
25
+ constructor(options: SessionManagerOptions);
26
+ /**
27
+ * Get or create a session for a channel
28
+ */
29
+ getOrCreateSession(channelId: string): Promise<SessionResult>;
30
+ /**
31
+ * Update the last message timestamp for a session
32
+ */
33
+ touchSession(channelId: string): Promise<void>;
34
+ /**
35
+ * Get an existing session without creating one
36
+ */
37
+ getSession(channelId: string): Promise<ChannelSession | null>;
38
+ /**
39
+ * Store or update the session ID for a channel
40
+ */
41
+ setSession(channelId: string, sessionId: string): Promise<void>;
42
+ /**
43
+ * Clear a specific session
44
+ */
45
+ clearSession(channelId: string): Promise<boolean>;
46
+ /**
47
+ * Clean up all expired sessions
48
+ */
49
+ cleanupExpiredSessions(): Promise<number>;
50
+ /**
51
+ * Get the count of active (non-expired) sessions
52
+ */
53
+ getActiveSessionCount(): Promise<number>;
54
+ private generateSessionId;
55
+ private isSessionExpired;
56
+ private loadState;
57
+ private saveState;
58
+ private ensureDirectoryExists;
59
+ private generateTempPath;
60
+ private renameWithRetry;
61
+ }
62
+ //# sourceMappingURL=session-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/session-manager/session-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,OAAO,EACL,KAAK,qBAAqB,EAE1B,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,cAAc,EAIpB,MAAM,YAAY,CAAC;AAmBpB;;;;;;;GAOG;AACH,qBAAa,cAAe,YAAW,eAAe;IACpD,SAAgB,SAAS,EAAE,MAAM,CAAC;IAElC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuB;IAC9C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAGvC,OAAO,CAAC,KAAK,CAAkC;gBAEnC,OAAO,EAAE,qBAAqB;IAmB1C;;OAEG;IACG,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAkCnE;;OAEG;IACG,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBpD;;OAEG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAsBnE;;OAEG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBrE;;OAEG;IACG,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAevD;;OAEG;IACG,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC;IA0B/C;;OAEG;IACG,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC;IAkB9C,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,gBAAgB;YAOV,SAAS;YAkDT,SAAS;YAwBT,qBAAqB;IAcnC,OAAO,CAAC,gBAAgB;YAOV,eAAe;CA6B9B"}
@@ -0,0 +1,320 @@
1
+ /**
2
+ * Session manager for Slack channel conversations
3
+ *
4
+ * Provides per-channel session management for Claude conversations,
5
+ * enabling conversation context preservation across Slack channels.
6
+ *
7
+ * Sessions are stored at .herdctl/slack-sessions/<agent-name>.yaml
8
+ */
9
+ import { mkdir } from "node:fs/promises";
10
+ import { join, dirname } from "node:path";
11
+ import { randomUUID } from "node:crypto";
12
+ import { stringify as stringifyYaml, parse as parseYaml } from "yaml";
13
+ import { readFile, writeFile, rename, unlink } from "node:fs/promises";
14
+ import { randomBytes } from "node:crypto";
15
+ import { createLogger } from "@herdctl/core";
16
+ import { SlackSessionStateSchema, createInitialSessionState, } from "./types.js";
17
+ import { SessionStateReadError, SessionStateWriteError, SessionDirectoryCreateError, } from "./errors.js";
18
+ // =============================================================================
19
+ // Default Logger
20
+ // =============================================================================
21
+ function createDefaultLogger(agentName) {
22
+ return createLogger(`slack-session:${agentName}`);
23
+ }
24
+ // =============================================================================
25
+ // Session Manager Implementation
26
+ // =============================================================================
27
+ /**
28
+ * SessionManager manages per-channel Claude sessions for a Slack agent.
29
+ *
30
+ * Each agent has its own SessionManager instance, storing session mappings
31
+ * in a YAML file at .herdctl/slack-sessions/<agent-name>.yaml
32
+ *
33
+ * Sessions are keyed by channelId (matching Discord's approach).
34
+ */
35
+ export class SessionManager {
36
+ agentName;
37
+ stateDir;
38
+ sessionExpiryHours;
39
+ logger;
40
+ stateFilePath;
41
+ // In-memory cache of session state
42
+ state = null;
43
+ constructor(options) {
44
+ this.agentName = options.agentName;
45
+ this.stateDir = options.stateDir;
46
+ this.sessionExpiryHours = options.sessionExpiryHours ?? 24;
47
+ this.logger =
48
+ options.logger ?? createDefaultLogger(options.agentName);
49
+ // Compute state file path
50
+ this.stateFilePath = join(this.stateDir, "slack-sessions", `${this.agentName}.yaml`);
51
+ }
52
+ // ===========================================================================
53
+ // Public API
54
+ // ===========================================================================
55
+ /**
56
+ * Get or create a session for a channel
57
+ */
58
+ async getOrCreateSession(channelId) {
59
+ const existingSession = await this.getSession(channelId);
60
+ if (existingSession) {
61
+ this.logger.info("Resuming existing session", {
62
+ channelId,
63
+ sessionId: existingSession.sessionId,
64
+ });
65
+ return {
66
+ sessionId: existingSession.sessionId,
67
+ isNew: false,
68
+ };
69
+ }
70
+ // Create new session
71
+ const sessionId = this.generateSessionId();
72
+ const state = await this.loadState();
73
+ const now = new Date().toISOString();
74
+ state.channels[channelId] = {
75
+ sessionId,
76
+ lastMessageAt: now,
77
+ };
78
+ await this.saveState(state);
79
+ this.logger.info("Created new session", { channelId, sessionId });
80
+ return {
81
+ sessionId,
82
+ isNew: true,
83
+ };
84
+ }
85
+ /**
86
+ * Update the last message timestamp for a session
87
+ */
88
+ async touchSession(channelId) {
89
+ const state = await this.loadState();
90
+ const session = state.channels[channelId];
91
+ if (!session) {
92
+ this.logger.warn("Attempted to touch non-existent session", {
93
+ channelId,
94
+ });
95
+ return;
96
+ }
97
+ session.lastMessageAt = new Date().toISOString();
98
+ await this.saveState(state);
99
+ this.logger.debug("Touched session", {
100
+ channelId,
101
+ sessionId: session.sessionId,
102
+ });
103
+ }
104
+ /**
105
+ * Get an existing session without creating one
106
+ */
107
+ async getSession(channelId) {
108
+ const state = await this.loadState();
109
+ const session = state.channels[channelId];
110
+ if (!session) {
111
+ return null;
112
+ }
113
+ // Check if session is expired
114
+ if (this.isSessionExpired(session)) {
115
+ this.logger.info("Session expired", {
116
+ channelId,
117
+ sessionId: session.sessionId,
118
+ lastMessageAt: session.lastMessageAt,
119
+ expiryHours: this.sessionExpiryHours,
120
+ });
121
+ return null;
122
+ }
123
+ return session;
124
+ }
125
+ /**
126
+ * Store or update the session ID for a channel
127
+ */
128
+ async setSession(channelId, sessionId) {
129
+ const state = await this.loadState();
130
+ const now = new Date().toISOString();
131
+ const existingSession = state.channels[channelId];
132
+ state.channels[channelId] = {
133
+ sessionId,
134
+ lastMessageAt: now,
135
+ };
136
+ await this.saveState(state);
137
+ if (existingSession) {
138
+ this.logger.debug("Updated session", {
139
+ channelId,
140
+ oldSessionId: existingSession.sessionId,
141
+ newSessionId: sessionId,
142
+ });
143
+ }
144
+ else {
145
+ this.logger.info("Stored new session", { channelId, sessionId });
146
+ }
147
+ }
148
+ /**
149
+ * Clear a specific session
150
+ */
151
+ async clearSession(channelId) {
152
+ const state = await this.loadState();
153
+ if (!state.channels[channelId]) {
154
+ return false;
155
+ }
156
+ const sessionId = state.channels[channelId].sessionId;
157
+ delete state.channels[channelId];
158
+ await this.saveState(state);
159
+ this.logger.info("Cleared session", { channelId, sessionId });
160
+ return true;
161
+ }
162
+ /**
163
+ * Clean up all expired sessions
164
+ */
165
+ async cleanupExpiredSessions() {
166
+ const state = await this.loadState();
167
+ const channelIds = Object.keys(state.channels);
168
+ let cleanedUp = 0;
169
+ for (const channelId of channelIds) {
170
+ const session = state.channels[channelId];
171
+ if (this.isSessionExpired(session)) {
172
+ this.logger.debug("Cleaning up expired session", {
173
+ channelId,
174
+ sessionId: session.sessionId,
175
+ lastMessageAt: session.lastMessageAt,
176
+ });
177
+ delete state.channels[channelId];
178
+ cleanedUp++;
179
+ }
180
+ }
181
+ if (cleanedUp > 0) {
182
+ await this.saveState(state);
183
+ this.logger.info("Cleaned up expired sessions", { count: cleanedUp });
184
+ }
185
+ return cleanedUp;
186
+ }
187
+ /**
188
+ * Get the count of active (non-expired) sessions
189
+ */
190
+ async getActiveSessionCount() {
191
+ const state = await this.loadState();
192
+ let activeCount = 0;
193
+ for (const channelId of Object.keys(state.channels)) {
194
+ const session = state.channels[channelId];
195
+ if (!this.isSessionExpired(session)) {
196
+ activeCount++;
197
+ }
198
+ }
199
+ return activeCount;
200
+ }
201
+ // ===========================================================================
202
+ // Private Helpers
203
+ // ===========================================================================
204
+ generateSessionId() {
205
+ return `slack-${this.agentName}-${randomUUID()}`;
206
+ }
207
+ isSessionExpired(session) {
208
+ const lastMessageAt = new Date(session.lastMessageAt);
209
+ const now = new Date();
210
+ const expiryMs = this.sessionExpiryHours * 60 * 60 * 1000;
211
+ return now.getTime() - lastMessageAt.getTime() > expiryMs;
212
+ }
213
+ async loadState() {
214
+ if (this.state) {
215
+ return this.state;
216
+ }
217
+ let content;
218
+ try {
219
+ content = await readFile(this.stateFilePath, "utf-8");
220
+ }
221
+ catch (error) {
222
+ const code = error.code;
223
+ if (code === "ENOENT") {
224
+ this.state = createInitialSessionState(this.agentName);
225
+ return this.state;
226
+ }
227
+ throw new SessionStateReadError(this.agentName, this.stateFilePath, {
228
+ cause: error,
229
+ });
230
+ }
231
+ if (content.trim() === "") {
232
+ this.state = createInitialSessionState(this.agentName);
233
+ return this.state;
234
+ }
235
+ let parsed;
236
+ try {
237
+ parsed = parseYaml(content);
238
+ }
239
+ catch (error) {
240
+ this.logger.warn("Corrupted session state file, creating fresh state", {
241
+ error: error.message,
242
+ });
243
+ this.state = createInitialSessionState(this.agentName);
244
+ return this.state;
245
+ }
246
+ const validated = SlackSessionStateSchema.safeParse(parsed);
247
+ if (!validated.success) {
248
+ this.logger.warn("Corrupted session state file, creating fresh state", {
249
+ error: validated.error.message,
250
+ });
251
+ this.state = createInitialSessionState(this.agentName);
252
+ return this.state;
253
+ }
254
+ this.state = validated.data;
255
+ return this.state;
256
+ }
257
+ async saveState(state) {
258
+ await this.ensureDirectoryExists();
259
+ this.state = state;
260
+ const yamlContent = stringifyYaml(state, { indent: 2, lineWidth: 120 });
261
+ const tempPath = this.generateTempPath(this.stateFilePath);
262
+ try {
263
+ await writeFile(tempPath, yamlContent, "utf-8");
264
+ await this.renameWithRetry(tempPath, this.stateFilePath);
265
+ }
266
+ catch (error) {
267
+ try {
268
+ await unlink(tempPath);
269
+ }
270
+ catch {
271
+ // Ignore cleanup errors
272
+ }
273
+ throw new SessionStateWriteError(this.agentName, this.stateFilePath, {
274
+ cause: error,
275
+ });
276
+ }
277
+ }
278
+ async ensureDirectoryExists() {
279
+ const dir = dirname(this.stateFilePath);
280
+ try {
281
+ await mkdir(dir, { recursive: true });
282
+ }
283
+ catch (error) {
284
+ const code = error.code;
285
+ if (code !== "EEXIST") {
286
+ throw new SessionDirectoryCreateError(this.agentName, dir, {
287
+ cause: error,
288
+ });
289
+ }
290
+ }
291
+ }
292
+ generateTempPath(targetPath) {
293
+ const dir = dirname(targetPath);
294
+ const random = randomBytes(8).toString("hex");
295
+ const filename = `${this.agentName}.yaml`;
296
+ return join(dir, `.${filename}.tmp.${random}`);
297
+ }
298
+ async renameWithRetry(oldPath, newPath, maxRetries = 3, baseDelayMs = 50) {
299
+ let lastError;
300
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
301
+ try {
302
+ await rename(oldPath, newPath);
303
+ return;
304
+ }
305
+ catch (error) {
306
+ lastError = error;
307
+ const code = error.code;
308
+ if (code !== "EACCES" && code !== "EPERM") {
309
+ throw error;
310
+ }
311
+ if (attempt < maxRetries) {
312
+ const delay = baseDelayMs * Math.pow(2, attempt);
313
+ await new Promise((resolve) => setTimeout(resolve, delay));
314
+ }
315
+ }
316
+ }
317
+ throw lastError;
318
+ }
319
+ }
320
+ //# sourceMappingURL=session-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../src/session-manager/session-manager.ts"],"names":[],"mappings":"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"}