@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.
- package/dist/config/__tests__/agent.test.js +12 -12
- package/dist/config/__tests__/agent.test.js.map +1 -1
- package/dist/config/__tests__/loader.test.js +201 -4
- package/dist/config/__tests__/loader.test.js.map +1 -1
- package/dist/config/__tests__/merge.test.js +29 -4
- package/dist/config/__tests__/merge.test.js.map +1 -1
- package/dist/config/__tests__/parser.test.js +13 -13
- package/dist/config/__tests__/parser.test.js.map +1 -1
- package/dist/config/__tests__/schema.test.js +10 -10
- package/dist/config/__tests__/schema.test.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +2 -2
- package/dist/config/index.js.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +71 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/merge.d.ts +4 -1
- package/dist/config/merge.d.ts.map +1 -1
- package/dist/config/merge.js +16 -0
- package/dist/config/merge.js.map +1 -1
- package/dist/config/schema.d.ts +906 -89
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +109 -7
- package/dist/config/schema.js.map +1 -1
- package/dist/fleet-manager/__tests__/coverage.test.js +25 -24
- package/dist/fleet-manager/__tests__/coverage.test.js.map +1 -1
- package/dist/fleet-manager/__tests__/discord-manager.test.js +9 -2
- package/dist/fleet-manager/__tests__/discord-manager.test.js.map +1 -1
- package/dist/fleet-manager/__tests__/integration.test.js +27 -0
- package/dist/fleet-manager/__tests__/integration.test.js.map +1 -1
- package/dist/fleet-manager/__tests__/job-control.test.js +66 -0
- package/dist/fleet-manager/__tests__/job-control.test.js.map +1 -1
- package/dist/fleet-manager/__tests__/status-queries.test.js +12 -11
- package/dist/fleet-manager/__tests__/status-queries.test.js.map +1 -1
- package/dist/fleet-manager/config-reload.js +9 -9
- package/dist/fleet-manager/config-reload.js.map +1 -1
- package/dist/fleet-manager/discord-manager.d.ts.map +1 -1
- package/dist/fleet-manager/discord-manager.js +27 -4
- package/dist/fleet-manager/discord-manager.js.map +1 -1
- package/dist/fleet-manager/fleet-manager.d.ts +11 -0
- package/dist/fleet-manager/fleet-manager.d.ts.map +1 -1
- package/dist/fleet-manager/fleet-manager.js +27 -0
- package/dist/fleet-manager/fleet-manager.js.map +1 -1
- package/dist/fleet-manager/job-control.d.ts +1 -1
- package/dist/fleet-manager/job-control.d.ts.map +1 -1
- package/dist/fleet-manager/job-control.js +36 -14
- package/dist/fleet-manager/job-control.js.map +1 -1
- package/dist/fleet-manager/schedule-executor.d.ts +1 -1
- package/dist/fleet-manager/schedule-executor.d.ts.map +1 -1
- package/dist/fleet-manager/schedule-executor.js +11 -14
- package/dist/fleet-manager/schedule-executor.js.map +1 -1
- package/dist/fleet-manager/status-queries.js +7 -7
- package/dist/fleet-manager/status-queries.js.map +1 -1
- package/dist/fleet-manager/types.d.ts +10 -2
- package/dist/fleet-manager/types.d.ts.map +1 -1
- package/dist/fleet-manager/working-directory-helper.d.ts +29 -0
- package/dist/fleet-manager/working-directory-helper.d.ts.map +1 -0
- package/dist/fleet-manager/working-directory-helper.js +36 -0
- package/dist/fleet-manager/working-directory-helper.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/runner/__tests__/job-executor.test.js +449 -118
- package/dist/runner/__tests__/job-executor.test.js.map +1 -1
- package/dist/runner/__tests__/sdk-adapter.test.js +147 -23
- package/dist/runner/__tests__/sdk-adapter.test.js.map +1 -1
- package/dist/runner/index.d.ts +2 -0
- package/dist/runner/index.d.ts.map +1 -1
- package/dist/runner/index.js +1 -0
- package/dist/runner/index.js.map +1 -1
- package/dist/runner/job-executor.d.ts +12 -8
- package/dist/runner/job-executor.d.ts.map +1 -1
- package/dist/runner/job-executor.js +257 -126
- package/dist/runner/job-executor.js.map +1 -1
- package/dist/runner/runtime/__tests__/cli-session-path.test.d.ts +2 -0
- package/dist/runner/runtime/__tests__/cli-session-path.test.d.ts.map +1 -0
- package/dist/runner/runtime/__tests__/cli-session-path.test.js +150 -0
- package/dist/runner/runtime/__tests__/cli-session-path.test.js.map +1 -0
- package/dist/runner/runtime/__tests__/docker-config.test.d.ts +2 -0
- package/dist/runner/runtime/__tests__/docker-config.test.d.ts.map +1 -0
- package/dist/runner/runtime/__tests__/docker-config.test.js +352 -0
- package/dist/runner/runtime/__tests__/docker-config.test.js.map +1 -0
- package/dist/runner/runtime/__tests__/docker-security.test.d.ts +2 -0
- package/dist/runner/runtime/__tests__/docker-security.test.d.ts.map +1 -0
- package/dist/runner/runtime/__tests__/docker-security.test.js +384 -0
- package/dist/runner/runtime/__tests__/docker-security.test.js.map +1 -0
- package/dist/runner/runtime/__tests__/factory.test.d.ts +2 -0
- package/dist/runner/runtime/__tests__/factory.test.d.ts.map +1 -0
- package/dist/runner/runtime/__tests__/factory.test.js +149 -0
- package/dist/runner/runtime/__tests__/factory.test.js.map +1 -0
- package/dist/runner/runtime/__tests__/integration.test.d.ts +2 -0
- package/dist/runner/runtime/__tests__/integration.test.d.ts.map +1 -0
- package/dist/runner/runtime/__tests__/integration.test.js +274 -0
- package/dist/runner/runtime/__tests__/integration.test.js.map +1 -0
- package/dist/runner/runtime/cli-runtime.d.ts +107 -0
- package/dist/runner/runtime/cli-runtime.d.ts.map +1 -0
- package/dist/runner/runtime/cli-runtime.js +335 -0
- package/dist/runner/runtime/cli-runtime.js.map +1 -0
- package/dist/runner/runtime/cli-session-path.d.ts +108 -0
- package/dist/runner/runtime/cli-session-path.d.ts.map +1 -0
- package/dist/runner/runtime/cli-session-path.js +173 -0
- package/dist/runner/runtime/cli-session-path.js.map +1 -0
- package/dist/runner/runtime/cli-session-watcher.d.ts +55 -0
- package/dist/runner/runtime/cli-session-watcher.d.ts.map +1 -0
- package/dist/runner/runtime/cli-session-watcher.js +187 -0
- package/dist/runner/runtime/cli-session-watcher.js.map +1 -0
- package/dist/runner/runtime/container-manager.d.ts +76 -0
- package/dist/runner/runtime/container-manager.d.ts.map +1 -0
- package/dist/runner/runtime/container-manager.js +229 -0
- package/dist/runner/runtime/container-manager.js.map +1 -0
- package/dist/runner/runtime/container-runner.d.ts +62 -0
- package/dist/runner/runtime/container-runner.d.ts.map +1 -0
- package/dist/runner/runtime/container-runner.js +235 -0
- package/dist/runner/runtime/container-runner.js.map +1 -0
- package/dist/runner/runtime/docker-config.d.ts +100 -0
- package/dist/runner/runtime/docker-config.d.ts.map +1 -0
- package/dist/runner/runtime/docker-config.js +98 -0
- package/dist/runner/runtime/docker-config.js.map +1 -0
- package/dist/runner/runtime/factory.d.ts +63 -0
- package/dist/runner/runtime/factory.d.ts.map +1 -0
- package/dist/runner/runtime/factory.js +68 -0
- package/dist/runner/runtime/factory.js.map +1 -0
- package/dist/runner/runtime/index.d.ts +20 -0
- package/dist/runner/runtime/index.d.ts.map +1 -0
- package/dist/runner/runtime/index.js +21 -0
- package/dist/runner/runtime/index.js.map +1 -0
- package/dist/runner/runtime/interface.d.ts +59 -0
- package/dist/runner/runtime/interface.d.ts.map +1 -0
- package/dist/runner/runtime/interface.js +12 -0
- package/dist/runner/runtime/interface.js.map +1 -0
- package/dist/runner/runtime/sdk-runtime.d.ts +46 -0
- package/dist/runner/runtime/sdk-runtime.d.ts.map +1 -0
- package/dist/runner/runtime/sdk-runtime.js +63 -0
- package/dist/runner/runtime/sdk-runtime.js.map +1 -0
- package/dist/runner/sdk-adapter.d.ts +4 -0
- package/dist/runner/sdk-adapter.d.ts.map +1 -1
- package/dist/runner/sdk-adapter.js +35 -16
- package/dist/runner/sdk-adapter.js.map +1 -1
- package/dist/runner/types.d.ts +12 -6
- package/dist/runner/types.d.ts.map +1 -1
- package/dist/scheduler/__tests__/schedule-runner.test.js +61 -50
- package/dist/scheduler/__tests__/schedule-runner.test.js.map +1 -1
- package/dist/scheduler/schedule-runner.d.ts +1 -4
- package/dist/scheduler/schedule-runner.d.ts.map +1 -1
- package/dist/scheduler/schedule-runner.js +40 -8
- package/dist/scheduler/schedule-runner.js.map +1 -1
- package/dist/state/__tests__/session-schema.test.js +4 -0
- package/dist/state/__tests__/session-schema.test.js.map +1 -1
- package/dist/state/__tests__/session-validation.test.d.ts +2 -0
- package/dist/state/__tests__/session-validation.test.d.ts.map +1 -0
- package/dist/state/__tests__/session-validation.test.js +446 -0
- package/dist/state/__tests__/session-validation.test.js.map +1 -0
- package/dist/state/__tests__/session.test.js +68 -0
- package/dist/state/__tests__/session.test.js.map +1 -1
- package/dist/state/__tests__/working-directory-validation.test.d.ts +5 -0
- package/dist/state/__tests__/working-directory-validation.test.d.ts.map +1 -0
- package/dist/state/__tests__/working-directory-validation.test.js +101 -0
- package/dist/state/__tests__/working-directory-validation.test.js.map +1 -0
- package/dist/state/index.d.ts +2 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +4 -0
- package/dist/state/index.js.map +1 -1
- package/dist/state/schemas/session-info.d.ts +32 -0
- package/dist/state/schemas/session-info.d.ts.map +1 -1
- package/dist/state/schemas/session-info.js +22 -0
- package/dist/state/schemas/session-info.js.map +1 -1
- package/dist/state/session-validation.d.ts +202 -0
- package/dist/state/session-validation.d.ts.map +1 -0
- package/dist/state/session-validation.js +407 -0
- package/dist/state/session-validation.js.map +1 -0
- package/dist/state/session.d.ts +23 -3
- package/dist/state/session.d.ts.map +1 -1
- package/dist/state/session.js +41 -6
- package/dist/state/session.js.map +1 -1
- package/dist/state/working-directory-validation.d.ts +52 -0
- package/dist/state/working-directory-validation.d.ts.map +1 -0
- package/dist/state/working-directory-validation.js +81 -0
- package/dist/state/working-directory-validation.js.map +1 -0
- 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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"}
|
package/dist/state/session.d.ts
CHANGED
|
@@ -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
|
|
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>;
|