@kraki/tentacle 0.1.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 (54) hide show
  1. package/LICENSE +21 -0
  2. package/dist/adapters/base.d.ts +107 -0
  3. package/dist/adapters/base.js +32 -0
  4. package/dist/adapters/base.js.map +1 -0
  5. package/dist/adapters/copilot.d.ts +57 -0
  6. package/dist/adapters/copilot.js +489 -0
  7. package/dist/adapters/copilot.js.map +1 -0
  8. package/dist/adapters/index.d.ts +5 -0
  9. package/dist/adapters/index.js +4 -0
  10. package/dist/adapters/index.js.map +1 -0
  11. package/dist/banner-data.json +1 -0
  12. package/dist/banner.d.ts +7 -0
  13. package/dist/banner.js +187 -0
  14. package/dist/banner.js.map +1 -0
  15. package/dist/checks.d.ts +26 -0
  16. package/dist/checks.js +74 -0
  17. package/dist/checks.js.map +1 -0
  18. package/dist/cli.d.ts +15 -0
  19. package/dist/cli.js +306 -0
  20. package/dist/cli.js.map +1 -0
  21. package/dist/config.d.ts +38 -0
  22. package/dist/config.js +113 -0
  23. package/dist/config.js.map +1 -0
  24. package/dist/daemon-worker.d.ts +21 -0
  25. package/dist/daemon-worker.js +127 -0
  26. package/dist/daemon-worker.js.map +1 -0
  27. package/dist/daemon.d.ts +24 -0
  28. package/dist/daemon.js +163 -0
  29. package/dist/daemon.js.map +1 -0
  30. package/dist/index.d.ts +8 -0
  31. package/dist/index.js +11 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/key-manager.d.ts +28 -0
  34. package/dist/key-manager.js +61 -0
  35. package/dist/key-manager.js.map +1 -0
  36. package/dist/logger.d.ts +8 -0
  37. package/dist/logger.js +30 -0
  38. package/dist/logger.js.map +1 -0
  39. package/dist/pair.d.ts +32 -0
  40. package/dist/pair.js +131 -0
  41. package/dist/pair.js.map +1 -0
  42. package/dist/parse-permission.d.ts +25 -0
  43. package/dist/parse-permission.js +67 -0
  44. package/dist/parse-permission.js.map +1 -0
  45. package/dist/relay-client.d.ts +90 -0
  46. package/dist/relay-client.js +525 -0
  47. package/dist/relay-client.js.map +1 -0
  48. package/dist/session-manager.d.ts +85 -0
  49. package/dist/session-manager.js +218 -0
  50. package/dist/session-manager.js.map +1 -0
  51. package/dist/setup.d.ts +13 -0
  52. package/dist/setup.js +234 -0
  53. package/dist/setup.js.map +1 -0
  54. package/package.json +48 -0
@@ -0,0 +1,525 @@
1
+ /**
2
+ * Relay client — connects the tentacle to the head via WebSocket.
3
+ *
4
+ * Translates adapter events into protocol messages and sends them to the head.
5
+ * Receives consumer actions from the head and routes them to the adapter.
6
+ * Handles auth, reconnection, and session lifecycle.
7
+ */
8
+ import { WebSocket } from 'ws';
9
+ import { importPublicKey } from '@kraki/crypto';
10
+ import { createLogger } from './logger.js';
11
+ const logger = createLogger('relay-client');
12
+ export class RelayClient {
13
+ ws = null;
14
+ adapter;
15
+ sessionManager;
16
+ keyManager;
17
+ options;
18
+ state = 'disconnected';
19
+ reconnectAttempts = 0;
20
+ reconnectTimer = null;
21
+ authInfo = null;
22
+ e2eEnabled = false;
23
+ /** Cached consumer public keys for E2E encryption */
24
+ consumerKeys = new Map();
25
+ /** Messages queued when E2E is enabled but no consumer keys are available yet */
26
+ pendingE2eQueue = [];
27
+ /** Maps pre-generated sessionId → requestId for concurrent create_session correlation */
28
+ pendingRequestIds = new Map();
29
+ // Stale connection detection — tracks server pings to detect sleep/network changes
30
+ lastServerPingAt = 0;
31
+ staleCheckTimer = null;
32
+ /** How long without a server ping before we consider the connection stale (ms) */
33
+ static STALE_THRESHOLD = 60_000; // 60s (server pings every 30s, so 2 missed pings)
34
+ /** How often to check for stale connection (ms) */
35
+ static STALE_CHECK_INTERVAL = 10_000;
36
+ /** Called when relay state changes */
37
+ onStateChange = null;
38
+ /** Called on auth success */
39
+ onAuthenticated = null;
40
+ /** Called on fatal error (won't reconnect) */
41
+ onFatalError = null;
42
+ constructor(adapter, sessionManager, options, keyManager) {
43
+ this.adapter = adapter;
44
+ this.sessionManager = sessionManager;
45
+ this.options = options;
46
+ this.keyManager = keyManager ?? null;
47
+ this.wireAdapterEvents();
48
+ }
49
+ /**
50
+ * Connect to the relay. Auto-reconnects on disconnect.
51
+ */
52
+ connect() {
53
+ if (this.ws)
54
+ return;
55
+ this.setState('connecting');
56
+ const ws = new WebSocket(this.options.relayUrl);
57
+ this.ws = ws;
58
+ ws.on('open', () => {
59
+ this.setState('authenticating');
60
+ this.reconnectAttempts = 0;
61
+ this.lastServerPingAt = Date.now();
62
+ this.startStaleCheck();
63
+ const authMsg = {
64
+ type: 'auth',
65
+ token: this.options.token,
66
+ device: {
67
+ ...this.options.device,
68
+ publicKey: this.keyManager?.getCompactPublicKey(),
69
+ },
70
+ };
71
+ ws.send(JSON.stringify(authMsg));
72
+ });
73
+ ws.on('message', (data) => {
74
+ this.lastServerPingAt = Date.now();
75
+ try {
76
+ const msg = JSON.parse(data.toString());
77
+ this.handleMessage(msg);
78
+ }
79
+ catch {
80
+ // Ignore malformed messages from head
81
+ }
82
+ });
83
+ ws.on('close', () => {
84
+ this.stopStaleCheck();
85
+ this.ws = null;
86
+ this.setState('disconnected');
87
+ this.scheduleReconnect();
88
+ });
89
+ ws.on('error', () => {
90
+ // Error triggers close, which handles reconnect
91
+ });
92
+ // Track server pings for stale connection detection
93
+ ws.on('ping', () => {
94
+ this.lastServerPingAt = Date.now();
95
+ });
96
+ }
97
+ /**
98
+ * Disconnect from the relay. No reconnect.
99
+ */
100
+ disconnect() {
101
+ this.stopStaleCheck();
102
+ if (this.reconnectTimer) {
103
+ clearTimeout(this.reconnectTimer);
104
+ this.reconnectTimer = null;
105
+ }
106
+ if (this.ws) {
107
+ this.ws.close();
108
+ this.ws = null;
109
+ }
110
+ this.setState('disconnected');
111
+ }
112
+ /**
113
+ * Get current connection state.
114
+ */
115
+ getState() {
116
+ return this.state;
117
+ }
118
+ /**
119
+ * Get auth info from last successful connection.
120
+ */
121
+ getAuthInfo() {
122
+ return this.authInfo;
123
+ }
124
+ // ── Message handling ────────────────────────────────
125
+ handleMessage(msg) {
126
+ if (msg.type === 'auth_ok') {
127
+ this.authInfo = msg;
128
+ this.e2eEnabled = this.authInfo.e2e && !!this.keyManager;
129
+ // Cache consumer device public keys for E2E
130
+ if (this.e2eEnabled && this.authInfo.devices) {
131
+ this.updateConsumerKeys(this.authInfo.devices);
132
+ }
133
+ this.setState('connected');
134
+ this.onAuthenticated?.(this.authInfo);
135
+ this.resumeDisconnectedSessions();
136
+ return;
137
+ }
138
+ if (msg.type === 'auth_error') {
139
+ this.onFatalError?.(msg.message);
140
+ this.disconnect();
141
+ return;
142
+ }
143
+ if (msg.type === 'pong') {
144
+ return;
145
+ }
146
+ if (msg.type === 'head_notice') {
147
+ // Update consumer keys when devices change
148
+ if (this.e2eEnabled && msg.event === 'device_online') {
149
+ const dev = msg.data?.device;
150
+ const key = dev?.encryptionKey ?? dev?.publicKey;
151
+ if (dev && key) {
152
+ this.consumerKeys.set(dev.id, key);
153
+ this.flushE2eQueue();
154
+ }
155
+ }
156
+ if (msg.event === 'device_offline' || msg.event === 'device_removed') {
157
+ this.consumerKeys.delete(msg.data?.deviceId);
158
+ }
159
+ return;
160
+ }
161
+ // In E2E mode, incoming consumer messages may be encrypted
162
+ if (msg.type === 'encrypted' && this.keyManager && this.authInfo) {
163
+ try {
164
+ const decrypted = this.keyManager.decryptForMe({ iv: msg.iv, ciphertext: msg.ciphertext, tag: msg.tag, keys: msg.keys }, this.authInfo.deviceId);
165
+ const inner = JSON.parse(decrypted);
166
+ this.handleConsumerMessage(inner);
167
+ }
168
+ catch {
169
+ // Can't decrypt — not for us or corrupted
170
+ }
171
+ return;
172
+ }
173
+ // Plaintext consumer messages
174
+ this.handleConsumerMessage(msg);
175
+ }
176
+ handleConsumerMessage(msg) {
177
+ // create_session is special — no sessionId yet
178
+ if (msg.type === 'create_session') {
179
+ this.handleCreateSession(msg);
180
+ return;
181
+ }
182
+ const sessionId = msg.sessionId;
183
+ if (!sessionId)
184
+ return;
185
+ try {
186
+ switch (msg.type) {
187
+ case 'send_input':
188
+ // Broadcast user_message back to apps (round-trip confirmation)
189
+ this.send({
190
+ type: 'user_message',
191
+ sessionId,
192
+ payload: { content: msg.payload.text },
193
+ });
194
+ this.adapter.sendMessage(sessionId, msg.payload.text, msg.payload.attachments)
195
+ .catch((err) => logger.error({ err, sessionId }, 'sendMessage failed'));
196
+ break;
197
+ case 'approve':
198
+ this.adapter.respondToPermission(sessionId, msg.payload.permissionId, 'approve')
199
+ .catch((err) => logger.error({ err, sessionId }, 'respondToPermission failed'));
200
+ break;
201
+ case 'deny':
202
+ this.adapter.respondToPermission(sessionId, msg.payload.permissionId, 'deny')
203
+ .catch((err) => logger.error({ err, sessionId }, 'respondToPermission failed'));
204
+ break;
205
+ case 'always_allow':
206
+ this.adapter.respondToPermission(sessionId, msg.payload.permissionId, 'always_allow')
207
+ .catch((err) => logger.error({ err, sessionId }, 'respondToPermission failed'));
208
+ break;
209
+ case 'answer':
210
+ this.adapter.respondToQuestion(sessionId, msg.payload.questionId, msg.payload.answer, false)
211
+ .catch((err) => logger.error({ err, sessionId }, 'respondToQuestion failed'));
212
+ break;
213
+ case 'kill_session':
214
+ this.adapter.killSession(sessionId)
215
+ .catch((err) => logger.error({ err, sessionId }, 'killSession failed'));
216
+ break;
217
+ case 'abort_session':
218
+ this.adapter.abortSession(sessionId)
219
+ .catch((err) => logger.error({ err, sessionId }, 'abortSession failed'));
220
+ break;
221
+ default: {
222
+ // Handle extended message types (e.g. set_session_mode)
223
+ const ext = msg;
224
+ if (ext.type === 'set_session_mode') {
225
+ this.adapter.setSessionMode(sessionId, ext.payload.mode);
226
+ // Echo confirmation back so head stores it for replay
227
+ this.send({
228
+ type: 'session_mode_set',
229
+ sessionId,
230
+ payload: { mode: ext.payload.mode },
231
+ });
232
+ }
233
+ break;
234
+ }
235
+ }
236
+ }
237
+ catch (err) {
238
+ logger.error({ err, sessionId, type: msg.type }, 'handleConsumerMessage failed');
239
+ }
240
+ }
241
+ async handleCreateSession(msg) {
242
+ if (msg.type !== 'create_session')
243
+ return;
244
+ const { model, cwd, prompt, requestId } = msg.payload;
245
+ // Pre-generate a stable sessionId and map requestId BEFORE calling the adapter.
246
+ // This is concurrency-safe: each request gets its own unique key.
247
+ const preSessionId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
248
+ if (requestId) {
249
+ this.pendingRequestIds.set(preSessionId, requestId);
250
+ }
251
+ try {
252
+ const result = await this.adapter.createSession({ model, cwd: cwd || '/', sessionId: preSessionId });
253
+ // If an initial prompt was provided, send it to the new session
254
+ if (prompt && result.sessionId) {
255
+ await this.adapter.sendMessage(result.sessionId, prompt);
256
+ }
257
+ }
258
+ catch (err) {
259
+ this.pendingRequestIds.delete(preSessionId);
260
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
261
+ this.ws.send(JSON.stringify({
262
+ type: 'server_error',
263
+ message: `Failed to create session: ${err.message}`,
264
+ requestId,
265
+ }));
266
+ }
267
+ }
268
+ }
269
+ // ── Adapter event wiring ────────────────────────────
270
+ wireAdapterEvents() {
271
+ this.adapter.onSessionCreated = (event) => {
272
+ // Track in SessionManager if not already tracked (from resume)
273
+ if (!this.sessionManager.getMeta(event.sessionId)) {
274
+ this.sessionManager.createSession(event.agent, event.model, event.sessionId);
275
+ }
276
+ // Look up requestId by sessionId (set in handleCreateSession before adapter call)
277
+ const requestId = this.pendingRequestIds.get(event.sessionId);
278
+ if (requestId)
279
+ this.pendingRequestIds.delete(event.sessionId);
280
+ this.send({
281
+ type: 'session_created',
282
+ sessionId: event.sessionId,
283
+ payload: { agent: event.agent, model: event.model, requestId },
284
+ });
285
+ };
286
+ this.adapter.onMessage = (sessionId, event) => {
287
+ this.send({
288
+ type: 'agent_message',
289
+ sessionId,
290
+ payload: { content: event.content },
291
+ });
292
+ // Update context with latest state
293
+ this.sessionManager.updateContext(sessionId, {
294
+ lastUserMessage: '', // Will be set by send_input handler
295
+ });
296
+ };
297
+ this.adapter.onMessageDelta = (sessionId, event) => {
298
+ this.send({
299
+ type: 'agent_message_delta',
300
+ sessionId,
301
+ payload: { content: event.content },
302
+ });
303
+ };
304
+ this.adapter.onPermissionRequest = (sessionId, event) => {
305
+ this.send({
306
+ type: 'permission',
307
+ sessionId,
308
+ payload: {
309
+ id: event.id,
310
+ toolName: event.toolArgs.toolName,
311
+ args: event.toolArgs.args,
312
+ description: event.description,
313
+ },
314
+ });
315
+ };
316
+ // When a permission is auto-resolved (e.g. by Always Allow), notify relay
317
+ // so the web app can remove the blocking card
318
+ this.adapter.onPermissionAutoResolved = (sessionId, permissionId) => {
319
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
320
+ this.ws.send(JSON.stringify({
321
+ type: 'approve',
322
+ sessionId,
323
+ payload: { permissionId },
324
+ }));
325
+ }
326
+ };
327
+ this.adapter.onQuestionRequest = (sessionId, event) => {
328
+ this.send({
329
+ type: 'question',
330
+ sessionId,
331
+ payload: {
332
+ id: event.id,
333
+ question: event.question,
334
+ choices: event.choices,
335
+ },
336
+ });
337
+ };
338
+ this.adapter.onToolStart = (sessionId, event) => {
339
+ this.send({
340
+ type: 'tool_start',
341
+ sessionId,
342
+ payload: { toolName: event.toolName, args: event.args, toolCallId: event.toolCallId },
343
+ });
344
+ // Track key files from tool usage
345
+ if (event.toolName === 'read_file' || event.toolName === 'write_file') {
346
+ const path = event.args?.path;
347
+ if (path) {
348
+ const ctx = this.sessionManager.getContext(sessionId);
349
+ if (ctx) {
350
+ const files = new Set(ctx.keyFiles);
351
+ files.add(path);
352
+ this.sessionManager.updateContext(sessionId, { keyFiles: Array.from(files) });
353
+ }
354
+ }
355
+ }
356
+ };
357
+ this.adapter.onToolComplete = (sessionId, event) => {
358
+ this.send({
359
+ type: 'tool_complete',
360
+ sessionId,
361
+ payload: { toolName: event.toolName, args: {}, result: event.result, toolCallId: event.toolCallId },
362
+ });
363
+ };
364
+ this.adapter.onIdle = (sessionId) => {
365
+ this.send({ type: 'idle', sessionId, payload: {} });
366
+ };
367
+ this.adapter.onError = (sessionId, event) => {
368
+ this.send({
369
+ type: 'error',
370
+ sessionId,
371
+ payload: { message: event.message },
372
+ });
373
+ };
374
+ this.adapter.onSessionEnded = (sessionId, event) => {
375
+ this.sessionManager.endSession(sessionId, event.reason);
376
+ this.send({
377
+ type: 'session_ended',
378
+ sessionId,
379
+ payload: { reason: event.reason },
380
+ });
381
+ };
382
+ }
383
+ // ── Session resume on reconnect ─────────────────────
384
+ async resumeDisconnectedSessions() {
385
+ const resumable = this.sessionManager.getResumableSessions();
386
+ for (const meta of resumable) {
387
+ const result = this.sessionManager.resumeSession(meta.id);
388
+ if (result) {
389
+ try {
390
+ await this.adapter.resumeSession(meta.id, result.context);
391
+ // session_created will be fired by adapter's onSessionCreated callback
392
+ }
393
+ catch {
394
+ this.sessionManager.endSession(meta.id, 'resume_failed');
395
+ this.send({
396
+ type: 'session_ended',
397
+ sessionId: meta.id,
398
+ payload: { reason: 'resume_failed' },
399
+ });
400
+ }
401
+ }
402
+ }
403
+ }
404
+ // ── Send to relay ───────────────────────────────────
405
+ // TODO: Make send() accept a discriminated union of ProducerMessage types
406
+ // instead of Partial<ProducerMessage> so TypeScript enforces correct payload
407
+ // shape per message type (e.g. user_message must have payload.content).
408
+ send(msg) {
409
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
410
+ return;
411
+ if (this.e2eEnabled && this.keyManager) {
412
+ if (this.consumerKeys.size === 0) {
413
+ // No consumers online — queue (bounded to prevent memory growth)
414
+ if (this.pendingE2eQueue.length < 1000) {
415
+ this.pendingE2eQueue.push(msg);
416
+ }
417
+ else {
418
+ logger.warn({ type: msg.type }, 'E2E queue full (1000) — dropping message');
419
+ }
420
+ return;
421
+ }
422
+ this.sendEncrypted(msg);
423
+ return;
424
+ }
425
+ try {
426
+ this.ws.send(JSON.stringify(msg));
427
+ }
428
+ catch (err) {
429
+ logger.error({ err }, 'ws.send failed');
430
+ }
431
+ }
432
+ /**
433
+ * Encrypt and send a message to the relay.
434
+ */
435
+ sendEncrypted(msg) {
436
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN || !this.keyManager)
437
+ return;
438
+ const recipients = [];
439
+ for (const [deviceId, compactKey] of this.consumerKeys) {
440
+ recipients.push({ deviceId, publicKey: importPublicKey(compactKey) });
441
+ }
442
+ const plaintext = JSON.stringify(msg);
443
+ const encrypted = this.keyManager.encryptForRecipients(plaintext, recipients);
444
+ const envelope = {
445
+ type: 'encrypted',
446
+ sessionId: msg.sessionId,
447
+ iv: encrypted.iv,
448
+ ciphertext: encrypted.ciphertext,
449
+ tag: encrypted.tag,
450
+ keys: encrypted.keys,
451
+ };
452
+ // Expose agent/model for session_created so the head can register properly
453
+ if (msg.type === 'session_created' && msg.payload) {
454
+ envelope.agent = msg.payload.agent;
455
+ envelope.model = msg.payload.model;
456
+ }
457
+ // Mark ephemeral messages — head forwards but doesn't persist
458
+ if (msg.type === 'agent_message_delta' || msg.type === 'idle') {
459
+ envelope.ephemeral = true;
460
+ }
461
+ this.ws.send(JSON.stringify(envelope));
462
+ }
463
+ /**
464
+ * Flush queued E2E messages once consumer keys become available.
465
+ */
466
+ flushE2eQueue() {
467
+ if (this.consumerKeys.size === 0 || this.pendingE2eQueue.length === 0)
468
+ return;
469
+ const queued = this.pendingE2eQueue.splice(0);
470
+ for (const msg of queued) {
471
+ this.sendEncrypted(msg);
472
+ }
473
+ }
474
+ updateConsumerKeys(devices) {
475
+ this.consumerKeys.clear();
476
+ for (const d of devices) {
477
+ if (d.role === 'app') {
478
+ const key = d.encryptionKey ?? d.publicKey;
479
+ if (key)
480
+ this.consumerKeys.set(d.id, key);
481
+ }
482
+ }
483
+ // Flush queued messages now that we have consumer keys
484
+ this.flushE2eQueue();
485
+ }
486
+ // ── Stale connection detection ───────────────────────
487
+ startStaleCheck() {
488
+ this.stopStaleCheck();
489
+ this.staleCheckTimer = setInterval(() => {
490
+ if (this.state !== 'connected' && this.state !== 'authenticating')
491
+ return;
492
+ const elapsed = Date.now() - this.lastServerPingAt;
493
+ if (elapsed > RelayClient.STALE_THRESHOLD) {
494
+ logger.warn(`No server ping for ${Math.round(elapsed / 1000)}s — connection stale, reconnecting`);
495
+ this.ws?.close();
496
+ }
497
+ }, RelayClient.STALE_CHECK_INTERVAL);
498
+ }
499
+ stopStaleCheck() {
500
+ if (this.staleCheckTimer) {
501
+ clearInterval(this.staleCheckTimer);
502
+ this.staleCheckTimer = null;
503
+ }
504
+ }
505
+ // ── Reconnect logic ─────────────────────────────────
506
+ scheduleReconnect() {
507
+ const max = this.options.maxReconnects ?? Infinity;
508
+ if (this.reconnectAttempts >= max) {
509
+ this.onFatalError?.('Max reconnect attempts reached');
510
+ return;
511
+ }
512
+ const delay = this.options.reconnectDelay ?? 3000;
513
+ this.reconnectTimer = setTimeout(() => {
514
+ this.reconnectAttempts++;
515
+ this.connect();
516
+ }, delay);
517
+ }
518
+ setState(state) {
519
+ if (this.state === state)
520
+ return;
521
+ this.state = state;
522
+ this.onStateChange?.(state);
523
+ }
524
+ }
525
+ //# sourceMappingURL=relay-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-client.js","sourceRoot":"","sources":["../src/relay-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Tentacle session manager.
3
+ *
4
+ * Owns session lifecycle: create, resume, crash recovery, context persistence.
5
+ * The head doesn't know about runs or context — it just sees sessionIds.
6
+ * This is the tentacle's local intelligence layer.
7
+ */
8
+ export interface SessionContext {
9
+ summary: string;
10
+ keyFiles: string[];
11
+ lastUserMessage: string;
12
+ pendingAction?: string;
13
+ updatedAt: string;
14
+ }
15
+ export interface SessionMeta {
16
+ id: string;
17
+ agent: string;
18
+ model?: string;
19
+ title?: string;
20
+ state: 'active' | 'idle' | 'disconnected' | 'ended';
21
+ currentRunId: string;
22
+ totalRuns: number;
23
+ createdAt: string;
24
+ updatedAt: string;
25
+ }
26
+ export interface RunRecord {
27
+ id: string;
28
+ startedAt: string;
29
+ endedAt?: string;
30
+ endReason?: string;
31
+ }
32
+ export declare class SessionManager {
33
+ private sessionsDir;
34
+ constructor(sessionsDir?: string);
35
+ /**
36
+ * Create a new session. Returns the session ID.
37
+ */
38
+ createSession(agent: string, model?: string, sessionId?: string): {
39
+ sessionId: string;
40
+ runId: string;
41
+ };
42
+ /**
43
+ * Resume a session after crash/restart. Creates a new run.
44
+ * Returns context for the agent to recover.
45
+ */
46
+ resumeSession(sessionId: string): {
47
+ runId: string;
48
+ context: SessionContext;
49
+ } | null;
50
+ /**
51
+ * Mark a session as ended normally.
52
+ */
53
+ endSession(sessionId: string, reason: string): void;
54
+ /**
55
+ * Mark a session as disconnected (agent process died unexpectedly).
56
+ */
57
+ markDisconnected(sessionId: string): void;
58
+ /**
59
+ * Update session context (rolling summary for recovery).
60
+ */
61
+ updateContext(sessionId: string, context: Partial<SessionContext>): void;
62
+ /**
63
+ * Update session title (e.g., auto-generated by agent).
64
+ */
65
+ setTitle(sessionId: string, title: string): void;
66
+ /**
67
+ * Get all sessions that were active/disconnected (need resume on restart).
68
+ */
69
+ getResumableSessions(): SessionMeta[];
70
+ /**
71
+ * Get session context for a session.
72
+ */
73
+ getContext(sessionId: string): SessionContext | null;
74
+ /**
75
+ * Get session metadata.
76
+ */
77
+ getMeta(sessionId: string): SessionMeta | null;
78
+ private sessionDir;
79
+ private readMeta;
80
+ private writeMeta;
81
+ private readContext;
82
+ private writeContext;
83
+ private readRun;
84
+ private writeRun;
85
+ }