@herdctl/core 1.3.0 → 2.0.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 (181) hide show
  1. package/dist/config/__tests__/agent.test.js +12 -12
  2. package/dist/config/__tests__/agent.test.js.map +1 -1
  3. package/dist/config/__tests__/loader.test.js +201 -4
  4. package/dist/config/__tests__/loader.test.js.map +1 -1
  5. package/dist/config/__tests__/merge.test.js +29 -4
  6. package/dist/config/__tests__/merge.test.js.map +1 -1
  7. package/dist/config/__tests__/parser.test.js +13 -13
  8. package/dist/config/__tests__/parser.test.js.map +1 -1
  9. package/dist/config/__tests__/schema.test.js +10 -10
  10. package/dist/config/__tests__/schema.test.js.map +1 -1
  11. package/dist/config/index.d.ts +1 -1
  12. package/dist/config/index.d.ts.map +1 -1
  13. package/dist/config/index.js +2 -2
  14. package/dist/config/index.js.map +1 -1
  15. package/dist/config/loader.d.ts.map +1 -1
  16. package/dist/config/loader.js +71 -0
  17. package/dist/config/loader.js.map +1 -1
  18. package/dist/config/merge.d.ts +4 -1
  19. package/dist/config/merge.d.ts.map +1 -1
  20. package/dist/config/merge.js +16 -0
  21. package/dist/config/merge.js.map +1 -1
  22. package/dist/config/schema.d.ts +906 -89
  23. package/dist/config/schema.d.ts.map +1 -1
  24. package/dist/config/schema.js +109 -7
  25. package/dist/config/schema.js.map +1 -1
  26. package/dist/fleet-manager/__tests__/coverage.test.js +25 -24
  27. package/dist/fleet-manager/__tests__/coverage.test.js.map +1 -1
  28. package/dist/fleet-manager/__tests__/discord-manager.test.js +9 -2
  29. package/dist/fleet-manager/__tests__/discord-manager.test.js.map +1 -1
  30. package/dist/fleet-manager/__tests__/integration.test.js +27 -0
  31. package/dist/fleet-manager/__tests__/integration.test.js.map +1 -1
  32. package/dist/fleet-manager/__tests__/job-control.test.js +66 -0
  33. package/dist/fleet-manager/__tests__/job-control.test.js.map +1 -1
  34. package/dist/fleet-manager/__tests__/status-queries.test.js +12 -11
  35. package/dist/fleet-manager/__tests__/status-queries.test.js.map +1 -1
  36. package/dist/fleet-manager/config-reload.js +9 -9
  37. package/dist/fleet-manager/config-reload.js.map +1 -1
  38. package/dist/fleet-manager/discord-manager.d.ts.map +1 -1
  39. package/dist/fleet-manager/discord-manager.js +27 -4
  40. package/dist/fleet-manager/discord-manager.js.map +1 -1
  41. package/dist/fleet-manager/fleet-manager.d.ts +11 -0
  42. package/dist/fleet-manager/fleet-manager.d.ts.map +1 -1
  43. package/dist/fleet-manager/fleet-manager.js +27 -0
  44. package/dist/fleet-manager/fleet-manager.js.map +1 -1
  45. package/dist/fleet-manager/job-control.d.ts +1 -1
  46. package/dist/fleet-manager/job-control.d.ts.map +1 -1
  47. package/dist/fleet-manager/job-control.js +36 -14
  48. package/dist/fleet-manager/job-control.js.map +1 -1
  49. package/dist/fleet-manager/schedule-executor.d.ts +1 -1
  50. package/dist/fleet-manager/schedule-executor.d.ts.map +1 -1
  51. package/dist/fleet-manager/schedule-executor.js +11 -14
  52. package/dist/fleet-manager/schedule-executor.js.map +1 -1
  53. package/dist/fleet-manager/status-queries.js +7 -7
  54. package/dist/fleet-manager/status-queries.js.map +1 -1
  55. package/dist/fleet-manager/types.d.ts +10 -2
  56. package/dist/fleet-manager/types.d.ts.map +1 -1
  57. package/dist/fleet-manager/working-directory-helper.d.ts +29 -0
  58. package/dist/fleet-manager/working-directory-helper.d.ts.map +1 -0
  59. package/dist/fleet-manager/working-directory-helper.js +36 -0
  60. package/dist/fleet-manager/working-directory-helper.js.map +1 -0
  61. package/dist/index.d.ts +2 -1
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +4 -1
  64. package/dist/index.js.map +1 -1
  65. package/dist/runner/__tests__/job-executor.test.js +449 -118
  66. package/dist/runner/__tests__/job-executor.test.js.map +1 -1
  67. package/dist/runner/__tests__/sdk-adapter.test.js +147 -23
  68. package/dist/runner/__tests__/sdk-adapter.test.js.map +1 -1
  69. package/dist/runner/index.d.ts +2 -0
  70. package/dist/runner/index.d.ts.map +1 -1
  71. package/dist/runner/index.js +1 -0
  72. package/dist/runner/index.js.map +1 -1
  73. package/dist/runner/job-executor.d.ts +12 -8
  74. package/dist/runner/job-executor.d.ts.map +1 -1
  75. package/dist/runner/job-executor.js +257 -126
  76. package/dist/runner/job-executor.js.map +1 -1
  77. package/dist/runner/runtime/__tests__/cli-session-path.test.d.ts +2 -0
  78. package/dist/runner/runtime/__tests__/cli-session-path.test.d.ts.map +1 -0
  79. package/dist/runner/runtime/__tests__/cli-session-path.test.js +150 -0
  80. package/dist/runner/runtime/__tests__/cli-session-path.test.js.map +1 -0
  81. package/dist/runner/runtime/__tests__/docker-config.test.d.ts +2 -0
  82. package/dist/runner/runtime/__tests__/docker-config.test.d.ts.map +1 -0
  83. package/dist/runner/runtime/__tests__/docker-config.test.js +352 -0
  84. package/dist/runner/runtime/__tests__/docker-config.test.js.map +1 -0
  85. package/dist/runner/runtime/__tests__/docker-security.test.d.ts +2 -0
  86. package/dist/runner/runtime/__tests__/docker-security.test.d.ts.map +1 -0
  87. package/dist/runner/runtime/__tests__/docker-security.test.js +384 -0
  88. package/dist/runner/runtime/__tests__/docker-security.test.js.map +1 -0
  89. package/dist/runner/runtime/__tests__/factory.test.d.ts +2 -0
  90. package/dist/runner/runtime/__tests__/factory.test.d.ts.map +1 -0
  91. package/dist/runner/runtime/__tests__/factory.test.js +149 -0
  92. package/dist/runner/runtime/__tests__/factory.test.js.map +1 -0
  93. package/dist/runner/runtime/__tests__/integration.test.d.ts +2 -0
  94. package/dist/runner/runtime/__tests__/integration.test.d.ts.map +1 -0
  95. package/dist/runner/runtime/__tests__/integration.test.js +274 -0
  96. package/dist/runner/runtime/__tests__/integration.test.js.map +1 -0
  97. package/dist/runner/runtime/cli-runtime.d.ts +107 -0
  98. package/dist/runner/runtime/cli-runtime.d.ts.map +1 -0
  99. package/dist/runner/runtime/cli-runtime.js +335 -0
  100. package/dist/runner/runtime/cli-runtime.js.map +1 -0
  101. package/dist/runner/runtime/cli-session-path.d.ts +108 -0
  102. package/dist/runner/runtime/cli-session-path.d.ts.map +1 -0
  103. package/dist/runner/runtime/cli-session-path.js +173 -0
  104. package/dist/runner/runtime/cli-session-path.js.map +1 -0
  105. package/dist/runner/runtime/cli-session-watcher.d.ts +55 -0
  106. package/dist/runner/runtime/cli-session-watcher.d.ts.map +1 -0
  107. package/dist/runner/runtime/cli-session-watcher.js +187 -0
  108. package/dist/runner/runtime/cli-session-watcher.js.map +1 -0
  109. package/dist/runner/runtime/container-manager.d.ts +76 -0
  110. package/dist/runner/runtime/container-manager.d.ts.map +1 -0
  111. package/dist/runner/runtime/container-manager.js +229 -0
  112. package/dist/runner/runtime/container-manager.js.map +1 -0
  113. package/dist/runner/runtime/container-runner.d.ts +62 -0
  114. package/dist/runner/runtime/container-runner.d.ts.map +1 -0
  115. package/dist/runner/runtime/container-runner.js +235 -0
  116. package/dist/runner/runtime/container-runner.js.map +1 -0
  117. package/dist/runner/runtime/docker-config.d.ts +100 -0
  118. package/dist/runner/runtime/docker-config.d.ts.map +1 -0
  119. package/dist/runner/runtime/docker-config.js +98 -0
  120. package/dist/runner/runtime/docker-config.js.map +1 -0
  121. package/dist/runner/runtime/factory.d.ts +63 -0
  122. package/dist/runner/runtime/factory.d.ts.map +1 -0
  123. package/dist/runner/runtime/factory.js +68 -0
  124. package/dist/runner/runtime/factory.js.map +1 -0
  125. package/dist/runner/runtime/index.d.ts +20 -0
  126. package/dist/runner/runtime/index.d.ts.map +1 -0
  127. package/dist/runner/runtime/index.js +21 -0
  128. package/dist/runner/runtime/index.js.map +1 -0
  129. package/dist/runner/runtime/interface.d.ts +59 -0
  130. package/dist/runner/runtime/interface.d.ts.map +1 -0
  131. package/dist/runner/runtime/interface.js +12 -0
  132. package/dist/runner/runtime/interface.js.map +1 -0
  133. package/dist/runner/runtime/sdk-runtime.d.ts +46 -0
  134. package/dist/runner/runtime/sdk-runtime.d.ts.map +1 -0
  135. package/dist/runner/runtime/sdk-runtime.js +63 -0
  136. package/dist/runner/runtime/sdk-runtime.js.map +1 -0
  137. package/dist/runner/sdk-adapter.d.ts +4 -0
  138. package/dist/runner/sdk-adapter.d.ts.map +1 -1
  139. package/dist/runner/sdk-adapter.js +35 -16
  140. package/dist/runner/sdk-adapter.js.map +1 -1
  141. package/dist/runner/types.d.ts +12 -6
  142. package/dist/runner/types.d.ts.map +1 -1
  143. package/dist/scheduler/__tests__/schedule-runner.test.js +61 -50
  144. package/dist/scheduler/__tests__/schedule-runner.test.js.map +1 -1
  145. package/dist/scheduler/schedule-runner.d.ts +1 -4
  146. package/dist/scheduler/schedule-runner.d.ts.map +1 -1
  147. package/dist/scheduler/schedule-runner.js +40 -8
  148. package/dist/scheduler/schedule-runner.js.map +1 -1
  149. package/dist/state/__tests__/session-schema.test.js +4 -0
  150. package/dist/state/__tests__/session-schema.test.js.map +1 -1
  151. package/dist/state/__tests__/session-validation.test.d.ts +2 -0
  152. package/dist/state/__tests__/session-validation.test.d.ts.map +1 -0
  153. package/dist/state/__tests__/session-validation.test.js +446 -0
  154. package/dist/state/__tests__/session-validation.test.js.map +1 -0
  155. package/dist/state/__tests__/session.test.js +68 -0
  156. package/dist/state/__tests__/session.test.js.map +1 -1
  157. package/dist/state/__tests__/working-directory-validation.test.d.ts +5 -0
  158. package/dist/state/__tests__/working-directory-validation.test.d.ts.map +1 -0
  159. package/dist/state/__tests__/working-directory-validation.test.js +101 -0
  160. package/dist/state/__tests__/working-directory-validation.test.js.map +1 -0
  161. package/dist/state/index.d.ts +2 -0
  162. package/dist/state/index.d.ts.map +1 -1
  163. package/dist/state/index.js +4 -0
  164. package/dist/state/index.js.map +1 -1
  165. package/dist/state/schemas/session-info.d.ts +32 -0
  166. package/dist/state/schemas/session-info.d.ts.map +1 -1
  167. package/dist/state/schemas/session-info.js +22 -0
  168. package/dist/state/schemas/session-info.js.map +1 -1
  169. package/dist/state/session-validation.d.ts +202 -0
  170. package/dist/state/session-validation.d.ts.map +1 -0
  171. package/dist/state/session-validation.js +407 -0
  172. package/dist/state/session-validation.js.map +1 -0
  173. package/dist/state/session.d.ts +23 -3
  174. package/dist/state/session.d.ts.map +1 -1
  175. package/dist/state/session.js +41 -6
  176. package/dist/state/session.js.map +1 -1
  177. package/dist/state/working-directory-validation.d.ts +52 -0
  178. package/dist/state/working-directory-validation.d.ts.map +1 -0
  179. package/dist/state/working-directory-validation.js +81 -0
  180. package/dist/state/working-directory-validation.js.map +1 -0
  181. package/package.json +7 -2
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Session validation utilities
3
+ *
4
+ * Provides timeout parsing and session expiration validation to prevent
5
+ * unexpected logouts when resuming expired sessions.
6
+ */
7
+ import type { SessionInfo } from "./schemas/session-info.js";
8
+ /**
9
+ * Result of session validation
10
+ */
11
+ export interface SessionValidationResult {
12
+ /** Whether the session is valid (not expired) */
13
+ valid: boolean;
14
+ /** If invalid, the reason why */
15
+ reason?: "expired" | "missing" | "invalid_timeout" | "file_not_found" | "runtime_mismatch";
16
+ /** Human-readable message */
17
+ message?: string;
18
+ /** Age of the session in milliseconds */
19
+ ageMs?: number;
20
+ /** Configured timeout in milliseconds */
21
+ timeoutMs?: number;
22
+ }
23
+ /**
24
+ * Parse a timeout string into milliseconds
25
+ *
26
+ * Supports formats:
27
+ * - "30s" - 30 seconds
28
+ * - "5m" - 5 minutes
29
+ * - "1h" - 1 hour
30
+ * - "1d" - 1 day
31
+ * - "1w" - 1 week
32
+ *
33
+ * @param timeout - Timeout string (e.g., "30m", "1h")
34
+ * @returns Timeout in milliseconds, or null if invalid format
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * parseTimeout("30m"); // 1800000 (30 minutes in ms)
39
+ * parseTimeout("1h"); // 3600000 (1 hour in ms)
40
+ * parseTimeout("24h"); // 86400000 (24 hours in ms)
41
+ * parseTimeout("invalid"); // null
42
+ * ```
43
+ */
44
+ export declare function parseTimeout(timeout: string): number | null;
45
+ /**
46
+ * Default session timeout if not configured (24 hours)
47
+ * This is a reasonable default that prevents stale sessions from being resumed
48
+ * while still allowing long-running work sessions.
49
+ */
50
+ export declare const DEFAULT_SESSION_TIMEOUT_MS: number;
51
+ /**
52
+ * Check if a CLI session file exists on disk
53
+ *
54
+ * @param workingDirectory - Working directory for the session
55
+ * @param sessionId - Session ID to check
56
+ * @returns true if the session file exists
57
+ */
58
+ export declare function cliSessionFileExists(workingDirectory: string, sessionId: string): Promise<boolean>;
59
+ /**
60
+ * Check if a session has expired based on its last_used_at timestamp
61
+ *
62
+ * @param session - The session info to validate
63
+ * @param timeout - Timeout string (e.g., "30m", "1h") or undefined for default
64
+ * @returns Validation result with expiration details
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * const session = await getSessionInfo(sessionsDir, agentName);
69
+ * if (session) {
70
+ * const validation = isSessionExpired(session, "1h");
71
+ * if (!validation.valid) {
72
+ * console.log(`Session expired: ${validation.message}`);
73
+ * // Clear the session and start fresh
74
+ * }
75
+ * }
76
+ * ```
77
+ */
78
+ export declare function validateSession(session: SessionInfo | null, timeout?: string): SessionValidationResult;
79
+ /**
80
+ * Validate a session including CLI session file existence check
81
+ *
82
+ * This async version of validateSession also checks if the CLI session file
83
+ * exists on disk (for CLI runtime sessions). Use this when validating sessions
84
+ * that will be resumed via CLI runtime.
85
+ *
86
+ * @param session - The session info to validate
87
+ * @param timeout - Timeout string (e.g., "30m", "1h") or undefined for default
88
+ * @returns Promise resolving to validation result
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * const session = await getSessionInfo(sessionsDir, agentName);
93
+ * if (session) {
94
+ * const validation = await validateSessionWithFileCheck(session, "1h");
95
+ * if (!validation.valid) {
96
+ * console.log(`Session invalid: ${validation.message}`);
97
+ * // Clear the session and start fresh
98
+ * }
99
+ * }
100
+ * ```
101
+ */
102
+ export declare function validateSessionWithFileCheck(session: SessionInfo | null, timeout?: string): Promise<SessionValidationResult>;
103
+ /**
104
+ * Validate that a session's runtime context matches the current agent configuration
105
+ *
106
+ * Sessions are tied to a specific runtime configuration (SDK vs CLI, Docker vs native).
107
+ * If the runtime context changes, the session must be invalidated because:
108
+ * - CLI sessions use different session file locations than SDK sessions
109
+ * - Docker sessions are isolated from native sessions (different filesystems)
110
+ * - Session IDs are not portable across runtime contexts
111
+ *
112
+ * @param session - The session info to validate
113
+ * @param currentRuntimeType - Current runtime type from agent config ("sdk" or "cli")
114
+ * @param currentDockerEnabled - Current Docker enabled state from agent config
115
+ * @returns Validation result indicating if runtime context matches
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * const session = await getSessionInfo(sessionsDir, agentName);
120
+ * if (session) {
121
+ * const validation = validateRuntimeContext(
122
+ * session,
123
+ * agent.runtime ?? "sdk",
124
+ * agent.docker?.enabled ?? false
125
+ * );
126
+ * if (!validation.valid) {
127
+ * console.log(`Runtime context mismatch: ${validation.message}`);
128
+ * await clearSession(sessionsDir, agentName);
129
+ * }
130
+ * }
131
+ * ```
132
+ */
133
+ export declare function validateRuntimeContext(session: SessionInfo | null, currentRuntimeType: "sdk" | "cli", currentDockerEnabled: boolean): SessionValidationResult;
134
+ /**
135
+ * Format a duration in milliseconds to a human-readable string
136
+ *
137
+ * @param ms - Duration in milliseconds
138
+ * @returns Human-readable duration string
139
+ */
140
+ export declare function formatDuration(ms: number): string;
141
+ /**
142
+ * Check if an error from the SDK indicates an expired or invalid session
143
+ *
144
+ * The SDK may return errors when trying to resume an expired session.
145
+ * This function helps identify such errors for proper handling.
146
+ *
147
+ * Common error patterns from Claude CLI/SDK:
148
+ * - "session expired" / "session_expired" - explicit expiration
149
+ * - "session not found" / "invalid session" - session doesn't exist on server
150
+ * - "resume failed" - generic resume failure
151
+ * - "conversation not found" / "no conversation" - conversation ID invalid
152
+ * - "cannot resume" / "unable to resume" - resume operation failed
153
+ * - "stale session" - session is too old
154
+ *
155
+ * Also checks error codes for structured error responses.
156
+ *
157
+ * @param error - The error to check
158
+ * @returns true if this appears to be a session expiration error
159
+ */
160
+ export declare function isSessionExpiredError(error: Error): boolean;
161
+ /**
162
+ * Result of cleaning up expired sessions
163
+ */
164
+ export interface CleanupResult {
165
+ /** Number of sessions that were expired and removed */
166
+ removed: number;
167
+ /** Number of valid sessions that were kept */
168
+ kept: number;
169
+ /** Names of agents whose sessions were removed */
170
+ removedAgents: string[];
171
+ }
172
+ /**
173
+ * Clean up expired sessions from the sessions directory
174
+ *
175
+ * This function checks all sessions and removes those that have expired
176
+ * based on the provided timeout. Useful for periodic cleanup or startup.
177
+ *
178
+ * @param sessionsDir - Path to the sessions directory
179
+ * @param timeout - Timeout string (e.g., "24h") or undefined for default
180
+ * @param options - Additional options
181
+ * @returns Result containing counts and removed agent names
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * import { cleanupExpiredSessions } from "./state";
186
+ *
187
+ * // Clean up sessions older than 24 hours (default)
188
+ * const result = await cleanupExpiredSessions(sessionsDir);
189
+ * console.log(`Removed ${result.removed} expired sessions`);
190
+ *
191
+ * // Clean up sessions older than 1 hour
192
+ * const result = await cleanupExpiredSessions(sessionsDir, "1h");
193
+ * ```
194
+ */
195
+ export declare function cleanupExpiredSessions(sessionsDir: string, timeout?: string, options?: {
196
+ logger?: {
197
+ info?: (msg: string) => void;
198
+ warn: (msg: string) => void;
199
+ };
200
+ dryRun?: boolean;
201
+ }): Promise<CleanupResult>;
202
+ //# sourceMappingURL=session-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-validation.d.ts","sourceRoot":"","sources":["../../src/state/session-validation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAM7D;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,iDAAiD;IACjD,KAAK,EAAE,OAAO,CAAC;IACf,iCAAiC;IACjC,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,iBAAiB,GAAG,gBAAgB,GAAG,kBAAkB,CAAC;IAC3F,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuB3D;AAED;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,QAAsB,CAAC;AAM9D;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,gBAAgB,EAAE,MAAM,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CAQlB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,WAAW,GAAG,IAAI,EAC3B,OAAO,CAAC,EAAE,MAAM,GACf,uBAAuB,CAuEzB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,WAAW,GAAG,IAAI,EAC3B,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,uBAAuB,CAAC,CA0BlC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,WAAW,GAAG,IAAI,EAC3B,kBAAkB,EAAE,KAAK,GAAG,KAAK,EACjC,oBAAoB,EAAE,OAAO,GAC5B,uBAAuB,CAiCzB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAgBjD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAsC3D;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,GAAE;IACP,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;IACvE,MAAM,CAAC,EAAE,OAAO,CAAC;CACb,GACL,OAAO,CAAC,aAAa,CAAC,CA4CxB"}
@@ -0,0 +1,407 @@
1
+ /**
2
+ * Session validation utilities
3
+ *
4
+ * Provides timeout parsing and session expiration validation to prevent
5
+ * unexpected logouts when resuming expired sessions.
6
+ */
7
+ import { access } from "node:fs/promises";
8
+ import { getCliSessionFile } from "../runner/runtime/cli-session-path.js";
9
+ // =============================================================================
10
+ // Timeout Parsing
11
+ // =============================================================================
12
+ /**
13
+ * Parse a timeout string into milliseconds
14
+ *
15
+ * Supports formats:
16
+ * - "30s" - 30 seconds
17
+ * - "5m" - 5 minutes
18
+ * - "1h" - 1 hour
19
+ * - "1d" - 1 day
20
+ * - "1w" - 1 week
21
+ *
22
+ * @param timeout - Timeout string (e.g., "30m", "1h")
23
+ * @returns Timeout in milliseconds, or null if invalid format
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * parseTimeout("30m"); // 1800000 (30 minutes in ms)
28
+ * parseTimeout("1h"); // 3600000 (1 hour in ms)
29
+ * parseTimeout("24h"); // 86400000 (24 hours in ms)
30
+ * parseTimeout("invalid"); // null
31
+ * ```
32
+ */
33
+ export function parseTimeout(timeout) {
34
+ const match = timeout.match(/^(\d+(?:\.\d+)?)(s|m|h|d|w)$/);
35
+ if (!match) {
36
+ return null;
37
+ }
38
+ const value = parseFloat(match[1]);
39
+ const unit = match[2];
40
+ switch (unit) {
41
+ case "s":
42
+ return value * 1000;
43
+ case "m":
44
+ return value * 60 * 1000;
45
+ case "h":
46
+ return value * 60 * 60 * 1000;
47
+ case "d":
48
+ return value * 24 * 60 * 60 * 1000;
49
+ case "w":
50
+ return value * 7 * 24 * 60 * 60 * 1000;
51
+ default:
52
+ return null;
53
+ }
54
+ }
55
+ /**
56
+ * Default session timeout if not configured (24 hours)
57
+ * This is a reasonable default that prevents stale sessions from being resumed
58
+ * while still allowing long-running work sessions.
59
+ */
60
+ export const DEFAULT_SESSION_TIMEOUT_MS = 24 * 60 * 60 * 1000; // 24 hours
61
+ // =============================================================================
62
+ // Session Validation
63
+ // =============================================================================
64
+ /**
65
+ * Check if a CLI session file exists on disk
66
+ *
67
+ * @param workingDirectory - Working directory for the session
68
+ * @param sessionId - Session ID to check
69
+ * @returns true if the session file exists
70
+ */
71
+ export async function cliSessionFileExists(workingDirectory, sessionId) {
72
+ try {
73
+ const sessionFile = getCliSessionFile(workingDirectory, sessionId);
74
+ await access(sessionFile);
75
+ return true;
76
+ }
77
+ catch {
78
+ return false;
79
+ }
80
+ }
81
+ /**
82
+ * Check if a session has expired based on its last_used_at timestamp
83
+ *
84
+ * @param session - The session info to validate
85
+ * @param timeout - Timeout string (e.g., "30m", "1h") or undefined for default
86
+ * @returns Validation result with expiration details
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * const session = await getSessionInfo(sessionsDir, agentName);
91
+ * if (session) {
92
+ * const validation = isSessionExpired(session, "1h");
93
+ * if (!validation.valid) {
94
+ * console.log(`Session expired: ${validation.message}`);
95
+ * // Clear the session and start fresh
96
+ * }
97
+ * }
98
+ * ```
99
+ */
100
+ export function validateSession(session, timeout) {
101
+ // Handle missing session
102
+ if (!session) {
103
+ return {
104
+ valid: false,
105
+ reason: "missing",
106
+ message: "No session found",
107
+ };
108
+ }
109
+ // Parse timeout or use default
110
+ let timeoutMs;
111
+ if (timeout) {
112
+ const parsed = parseTimeout(timeout);
113
+ if (parsed === null) {
114
+ return {
115
+ valid: false,
116
+ reason: "invalid_timeout",
117
+ message: `Invalid timeout format: "${timeout}". Expected format like "30m", "1h", "24h"`,
118
+ };
119
+ }
120
+ timeoutMs = parsed;
121
+ }
122
+ else {
123
+ timeoutMs = DEFAULT_SESSION_TIMEOUT_MS;
124
+ }
125
+ // Calculate session age from last_used_at
126
+ const lastUsedAt = new Date(session.last_used_at).getTime();
127
+ const now = Date.now();
128
+ const ageMs = now - lastUsedAt;
129
+ // Handle invalid date parsing (NaN) - treat as expired to force a fresh session
130
+ // This prevents unexpected behavior from corrupted session files
131
+ if (Number.isNaN(ageMs)) {
132
+ return {
133
+ valid: false,
134
+ reason: "expired",
135
+ message: `Session has invalid last_used_at timestamp: "${session.last_used_at}"`,
136
+ timeoutMs,
137
+ };
138
+ }
139
+ // Handle future timestamps (negative age) - could indicate clock skew or timezone issues
140
+ // Treat as valid but log the unusual state; the session will be refreshed on next use
141
+ if (ageMs < 0) {
142
+ return {
143
+ valid: true,
144
+ ageMs: 0, // Report as just used
145
+ timeoutMs,
146
+ };
147
+ }
148
+ // Check if expired
149
+ if (ageMs > timeoutMs) {
150
+ const ageMinutes = Math.round(ageMs / (60 * 1000));
151
+ const timeoutMinutes = Math.round(timeoutMs / (60 * 1000));
152
+ return {
153
+ valid: false,
154
+ reason: "expired",
155
+ message: `Session expired: last used ${formatDuration(ageMs)} ago, timeout is ${formatDuration(timeoutMs)}`,
156
+ ageMs,
157
+ timeoutMs,
158
+ };
159
+ }
160
+ return {
161
+ valid: true,
162
+ ageMs,
163
+ timeoutMs,
164
+ };
165
+ }
166
+ /**
167
+ * Validate a session including CLI session file existence check
168
+ *
169
+ * This async version of validateSession also checks if the CLI session file
170
+ * exists on disk (for CLI runtime sessions). Use this when validating sessions
171
+ * that will be resumed via CLI runtime.
172
+ *
173
+ * @param session - The session info to validate
174
+ * @param timeout - Timeout string (e.g., "30m", "1h") or undefined for default
175
+ * @returns Promise resolving to validation result
176
+ *
177
+ * @example
178
+ * ```typescript
179
+ * const session = await getSessionInfo(sessionsDir, agentName);
180
+ * if (session) {
181
+ * const validation = await validateSessionWithFileCheck(session, "1h");
182
+ * if (!validation.valid) {
183
+ * console.log(`Session invalid: ${validation.message}`);
184
+ * // Clear the session and start fresh
185
+ * }
186
+ * }
187
+ * ```
188
+ */
189
+ export async function validateSessionWithFileCheck(session, timeout) {
190
+ // First do basic validation (expiration, etc.)
191
+ const basicValidation = validateSession(session, timeout);
192
+ if (!basicValidation.valid) {
193
+ return basicValidation;
194
+ }
195
+ // If session exists and isn't expired, check if CLI session file exists
196
+ if (session && session.working_directory) {
197
+ const fileExists = await cliSessionFileExists(session.working_directory, session.session_id);
198
+ if (!fileExists) {
199
+ return {
200
+ valid: false,
201
+ reason: "file_not_found",
202
+ message: `CLI session file not found for session ${session.session_id}`,
203
+ ageMs: basicValidation.ageMs,
204
+ timeoutMs: basicValidation.timeoutMs,
205
+ };
206
+ }
207
+ }
208
+ return basicValidation;
209
+ }
210
+ /**
211
+ * Validate that a session's runtime context matches the current agent configuration
212
+ *
213
+ * Sessions are tied to a specific runtime configuration (SDK vs CLI, Docker vs native).
214
+ * If the runtime context changes, the session must be invalidated because:
215
+ * - CLI sessions use different session file locations than SDK sessions
216
+ * - Docker sessions are isolated from native sessions (different filesystems)
217
+ * - Session IDs are not portable across runtime contexts
218
+ *
219
+ * @param session - The session info to validate
220
+ * @param currentRuntimeType - Current runtime type from agent config ("sdk" or "cli")
221
+ * @param currentDockerEnabled - Current Docker enabled state from agent config
222
+ * @returns Validation result indicating if runtime context matches
223
+ *
224
+ * @example
225
+ * ```typescript
226
+ * const session = await getSessionInfo(sessionsDir, agentName);
227
+ * if (session) {
228
+ * const validation = validateRuntimeContext(
229
+ * session,
230
+ * agent.runtime ?? "sdk",
231
+ * agent.docker?.enabled ?? false
232
+ * );
233
+ * if (!validation.valid) {
234
+ * console.log(`Runtime context mismatch: ${validation.message}`);
235
+ * await clearSession(sessionsDir, agentName);
236
+ * }
237
+ * }
238
+ * ```
239
+ */
240
+ export function validateRuntimeContext(session, currentRuntimeType, currentDockerEnabled) {
241
+ // Handle missing session
242
+ if (!session) {
243
+ return {
244
+ valid: false,
245
+ reason: "missing",
246
+ message: "No session found",
247
+ };
248
+ }
249
+ // Check runtime type mismatch
250
+ if (session.runtime_type !== currentRuntimeType) {
251
+ return {
252
+ valid: false,
253
+ reason: "runtime_mismatch",
254
+ message: `Runtime type changed from "${session.runtime_type}" to "${currentRuntimeType}". Session must be recreated for the new runtime.`,
255
+ };
256
+ }
257
+ // Check Docker enabled mismatch
258
+ if (session.docker_enabled !== currentDockerEnabled) {
259
+ const oldContext = session.docker_enabled ? "Docker" : "native";
260
+ const newContext = currentDockerEnabled ? "Docker" : "native";
261
+ return {
262
+ valid: false,
263
+ reason: "runtime_mismatch",
264
+ message: `Docker context changed from ${oldContext} to ${newContext}. Session must be recreated for the new context.`,
265
+ };
266
+ }
267
+ return {
268
+ valid: true,
269
+ };
270
+ }
271
+ /**
272
+ * Format a duration in milliseconds to a human-readable string
273
+ *
274
+ * @param ms - Duration in milliseconds
275
+ * @returns Human-readable duration string
276
+ */
277
+ export function formatDuration(ms) {
278
+ const seconds = Math.floor(ms / 1000);
279
+ const minutes = Math.floor(seconds / 60);
280
+ const hours = Math.floor(minutes / 60);
281
+ const days = Math.floor(hours / 24);
282
+ if (days > 0) {
283
+ return `${days}d ${hours % 24}h`;
284
+ }
285
+ if (hours > 0) {
286
+ return `${hours}h ${minutes % 60}m`;
287
+ }
288
+ if (minutes > 0) {
289
+ return `${minutes}m`;
290
+ }
291
+ return `${seconds}s`;
292
+ }
293
+ /**
294
+ * Check if an error from the SDK indicates an expired or invalid session
295
+ *
296
+ * The SDK may return errors when trying to resume an expired session.
297
+ * This function helps identify such errors for proper handling.
298
+ *
299
+ * Common error patterns from Claude CLI/SDK:
300
+ * - "session expired" / "session_expired" - explicit expiration
301
+ * - "session not found" / "invalid session" - session doesn't exist on server
302
+ * - "resume failed" - generic resume failure
303
+ * - "conversation not found" / "no conversation" - conversation ID invalid
304
+ * - "cannot resume" / "unable to resume" - resume operation failed
305
+ * - "stale session" - session is too old
306
+ *
307
+ * Also checks error codes for structured error responses.
308
+ *
309
+ * @param error - The error to check
310
+ * @returns true if this appears to be a session expiration error
311
+ */
312
+ export function isSessionExpiredError(error) {
313
+ const message = error.message.toLowerCase();
314
+ // Check error codes for structured errors (e.g., from SDK responses)
315
+ const errorCode = error.code?.toLowerCase() ?? "";
316
+ if (errorCode === "session_expired" ||
317
+ errorCode === "invalid_session" ||
318
+ errorCode === "session_not_found" ||
319
+ errorCode === "conversation_expired" ||
320
+ errorCode === "conversation_not_found") {
321
+ return true;
322
+ }
323
+ return (
324
+ // Explicit session expiration
325
+ message.includes("session expired") ||
326
+ message.includes("session_expired") ||
327
+ message.includes("has expired") ||
328
+ message.includes("stale session") ||
329
+ // Session not found on server
330
+ message.includes("session not found") ||
331
+ message.includes("invalid session") ||
332
+ message.includes("session does not exist") ||
333
+ message.includes("session id") && message.includes("not found") ||
334
+ // Conversation/context issues (Claude CLI uses these)
335
+ message.includes("conversation not found") ||
336
+ message.includes("no conversation") ||
337
+ message.includes("conversation does not exist") ||
338
+ message.includes("invalid conversation") ||
339
+ // Resume operation failures
340
+ message.includes("resume failed") ||
341
+ message.includes("cannot resume") ||
342
+ message.includes("unable to resume") ||
343
+ message.includes("failed to resume") ||
344
+ message.includes("could not resume"));
345
+ }
346
+ /**
347
+ * Clean up expired sessions from the sessions directory
348
+ *
349
+ * This function checks all sessions and removes those that have expired
350
+ * based on the provided timeout. Useful for periodic cleanup or startup.
351
+ *
352
+ * @param sessionsDir - Path to the sessions directory
353
+ * @param timeout - Timeout string (e.g., "24h") or undefined for default
354
+ * @param options - Additional options
355
+ * @returns Result containing counts and removed agent names
356
+ *
357
+ * @example
358
+ * ```typescript
359
+ * import { cleanupExpiredSessions } from "./state";
360
+ *
361
+ * // Clean up sessions older than 24 hours (default)
362
+ * const result = await cleanupExpiredSessions(sessionsDir);
363
+ * console.log(`Removed ${result.removed} expired sessions`);
364
+ *
365
+ * // Clean up sessions older than 1 hour
366
+ * const result = await cleanupExpiredSessions(sessionsDir, "1h");
367
+ * ```
368
+ */
369
+ export async function cleanupExpiredSessions(sessionsDir, timeout, options = {}) {
370
+ const { logger = console, dryRun = false } = options;
371
+ // Import dynamically to avoid circular dependencies
372
+ const { listSessions, clearSession } = await import("./session.js");
373
+ const result = {
374
+ removed: 0,
375
+ kept: 0,
376
+ removedAgents: [],
377
+ };
378
+ // List all sessions
379
+ const sessions = await listSessions(sessionsDir, { logger });
380
+ for (const session of sessions) {
381
+ const validation = validateSession(session, timeout);
382
+ if (!validation.valid && validation.reason === "expired") {
383
+ if (!dryRun) {
384
+ try {
385
+ await clearSession(sessionsDir, session.agent_name);
386
+ result.removed++;
387
+ result.removedAgents.push(session.agent_name);
388
+ logger.info?.(`Cleaned up expired session for ${session.agent_name}`);
389
+ }
390
+ catch (error) {
391
+ logger.warn(`Failed to clean up session for ${session.agent_name}: ${error.message}`);
392
+ }
393
+ }
394
+ else {
395
+ // Dry run - just count
396
+ result.removed++;
397
+ result.removedAgents.push(session.agent_name);
398
+ logger.info?.(`[DRY RUN] Would clean up expired session for ${session.agent_name}`);
399
+ }
400
+ }
401
+ else {
402
+ result.kept++;
403
+ }
404
+ }
405
+ return result;
406
+ }
407
+ //# sourceMappingURL=session-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-validation.js","sourceRoot":"","sources":["../../src/state/session-validation.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,18 @@ import { type SessionInfo } from "./schemas/session-info.js";
11
11
  export interface SessionOptions {
12
12
  /** Logger for warnings */
13
13
  logger?: SessionLogger;
14
+ /**
15
+ * Session expiry timeout string (e.g., "24h", "30m").
16
+ * If provided, expired sessions will be treated as non-existent and automatically cleared.
17
+ * This prevents stale session IDs from being used for resume attempts.
18
+ */
19
+ timeout?: string;
20
+ /**
21
+ * Runtime type ("cli" or "sdk").
22
+ * If "cli", validates that CLI session files exist on disk.
23
+ * If "sdk" or undefined, only validates expiration (no file check).
24
+ */
25
+ runtime?: "cli" | "sdk";
14
26
  }
15
27
  /**
16
28
  * Logger interface for session operations
@@ -25,21 +37,29 @@ export type SessionInfoUpdates = Partial<Omit<SessionInfo, "agent_name" | "creat
25
37
  /**
26
38
  * Get session info for an agent
27
39
  *
28
- * Returns null if no session exists (handles missing file gracefully).
40
+ * Returns null if no session exists, is corrupted, or has expired.
41
+ * When a timeout is provided, expired sessions are automatically cleared
42
+ * to prevent stale session IDs from being used for resume attempts.
29
43
  *
30
44
  * @param sessionsDir - Path to the sessions directory
31
45
  * @param agentName - Name of the agent
32
- * @param options - Optional operation options
33
- * @returns The session info, or null if not found
46
+ * @param options - Optional operation options including timeout for expiry validation
47
+ * @returns The session info, or null if not found/expired
34
48
  *
35
49
  * @example
36
50
  * ```typescript
51
+ * // Get session without expiry validation
37
52
  * const session = await getSessionInfo('/path/to/.herdctl/sessions', 'my-agent');
38
53
  * if (session) {
39
54
  * console.log(`Session ${session.session_id} has ${session.job_count} jobs`);
40
55
  * } else {
41
56
  * console.log('No existing session');
42
57
  * }
58
+ *
59
+ * // Get session with 24-hour expiry validation (prevents unexpected logouts)
60
+ * const session = await getSessionInfo('/path/to/.herdctl/sessions', 'my-agent', {
61
+ * timeout: '24h' // Sessions older than 24 hours will be treated as expired
62
+ * });
43
63
  * ```
44
64
  */
45
65
  export declare function getSessionInfo(sessionsDir: string, agentName: string, options?: SessionOptions): Promise<SessionInfo | null>;