@cleocode/core 2026.3.70 → 2026.3.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Session snapshot serialization and restoration.
3
+ *
4
+ * Provides `serializeSession()` and `restoreSession()` for full session
5
+ * state capture and hydration. Designed for CleoOS agent session persistence:
6
+ * when an agent dies, CleoOS serializes the session snapshot, and when a new
7
+ * agent connects, it restores the snapshot to resume seamlessly.
8
+ *
9
+ * The snapshot captures everything needed to resume work:
10
+ * - Full session object (scope, taskWork, notes, stats)
11
+ * - Handoff data (completed tasks, decisions, next suggested)
12
+ * - Brain context (recent observations linked to this session)
13
+ * - Active task state (current focus, blockers)
14
+ *
15
+ * @module sessions/snapshot
16
+ */
17
+ import type { Session } from '@cleocode/contracts';
18
+ import type { DataAccessor } from '../store/data-accessor.js';
19
+ import { type HandoffData } from './handoff.js';
20
+ /** Version of the snapshot schema. Increment on breaking changes. */
21
+ export declare const SNAPSHOT_VERSION = 1;
22
+ /** A decision recorded during the session. */
23
+ export interface SnapshotDecision {
24
+ /** Decision text. */
25
+ decision: string;
26
+ /** Rationale for the decision. */
27
+ rationale: string;
28
+ /** Task ID context. */
29
+ taskId: string;
30
+ /** When the decision was recorded. */
31
+ recordedAt: string;
32
+ }
33
+ /** Brain observation linked to this session. */
34
+ export interface SnapshotObservation {
35
+ /** Observation ID. */
36
+ id: string;
37
+ /** Observation text. */
38
+ text: string;
39
+ /** Observation type (discovery, change, feature, etc.). */
40
+ type: string;
41
+ /** When the observation was created. */
42
+ createdAt: string;
43
+ }
44
+ /** Active task context at snapshot time. */
45
+ export interface SnapshotTaskContext {
46
+ /** Task ID currently in focus. */
47
+ taskId: string;
48
+ /** Task title. */
49
+ title: string;
50
+ /** Task status. */
51
+ status: string;
52
+ /** Task priority. */
53
+ priority: string;
54
+ /** Task description (truncated to save space). */
55
+ description: string;
56
+ /** Acceptance criteria if any. */
57
+ acceptance?: string;
58
+ }
59
+ /**
60
+ * Complete session snapshot — everything needed to resume.
61
+ *
62
+ * This is the serialization format. It is JSON-safe and can be stored
63
+ * in a file, database column, or transmitted over the network.
64
+ */
65
+ export interface SessionSnapshot {
66
+ /** Schema version for forward compatibility. */
67
+ version: number;
68
+ /** When the snapshot was created. */
69
+ capturedAt: string;
70
+ /** The full session object. */
71
+ session: Session;
72
+ /** Computed handoff data. */
73
+ handoff: HandoffData;
74
+ /** Decisions recorded in this session. */
75
+ decisions: SnapshotDecision[];
76
+ /** Recent brain observations linked to this session. */
77
+ observations: SnapshotObservation[];
78
+ /** Current task context (if a task is focused). */
79
+ activeTask: SnapshotTaskContext | null;
80
+ /** Session duration in minutes at snapshot time. */
81
+ durationMinutes: number;
82
+ }
83
+ /** Options for serializing a session. */
84
+ export interface SerializeOptions {
85
+ /** Session ID to serialize. If omitted, uses the active session. */
86
+ sessionId?: string;
87
+ /** Maximum number of brain observations to include. Default: 10. */
88
+ maxObservations?: number;
89
+ /** Maximum description length for active task. Default: 500. */
90
+ maxDescriptionLength?: number;
91
+ }
92
+ /** Options for restoring a session. */
93
+ export interface RestoreOptions {
94
+ /** Agent identifier for the new agent taking over. */
95
+ agent?: string;
96
+ /** Whether to resume the session (set status to active). Default: true. */
97
+ activate?: boolean;
98
+ }
99
+ /**
100
+ * Serialize a session into a complete snapshot.
101
+ *
102
+ * Captures the full session state including handoff data, decisions,
103
+ * brain observations, and active task context. The result is a
104
+ * JSON-serializable object that can be stored and later restored.
105
+ *
106
+ * @param projectRoot - Project root directory
107
+ * @param options - Serialization options
108
+ * @returns Complete session snapshot
109
+ */
110
+ export declare function serializeSession(projectRoot: string, options?: SerializeOptions, accessor?: DataAccessor): Promise<SessionSnapshot>;
111
+ /**
112
+ * Restore a session from a snapshot.
113
+ *
114
+ * Hydrates a session from a previously serialized snapshot. The session
115
+ * is re-inserted into the sessions store and optionally activated.
116
+ * Brain observations from the snapshot are NOT re-inserted (they already
117
+ * exist in brain.db) — only the session state is restored.
118
+ *
119
+ * @param projectRoot - Project root directory
120
+ * @param snapshot - The snapshot to restore from
121
+ * @param options - Restoration options
122
+ * @returns The restored session
123
+ */
124
+ export declare function restoreSession(projectRoot: string, snapshot: SessionSnapshot, options?: RestoreOptions, accessor?: DataAccessor): Promise<Session>;
125
+ //# sourceMappingURL=snapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/sessions/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAG9D,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAMhE,qEAAqE;AACrE,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,8CAA8C;AAC9C,MAAM,WAAW,gBAAgB;IAC/B,qBAAqB;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,gDAAgD;AAChD,MAAM,WAAW,mBAAmB;IAClC,sBAAsB;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,4CAA4C;AAC5C,MAAM,WAAW,mBAAmB;IAClC,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,mBAAmB;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,6BAA6B;IAC7B,OAAO,EAAE,WAAW,CAAC;IACrB,0CAA0C;IAC1C,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,wDAAwD;IACxD,YAAY,EAAE,mBAAmB,EAAE,CAAC;IACpC,mDAAmD;IACnD,UAAU,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACvC,oDAAoD;IACpD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,yCAAyC;AACzC,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gEAAgE;IAChE,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,uCAAuC;AACvC,MAAM,WAAW,cAAc;IAC7B,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAMD;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,gBAAqB,EAC9B,QAAQ,CAAC,EAAE,YAAY,GACtB,OAAO,CAAC,eAAe,CAAC,CAoG1B;AAMD;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,eAAe,EACzB,OAAO,GAAE,cAAmB,EAC5B,QAAQ,CAAC,EAAE,YAAY,GACtB,OAAO,CAAC,OAAO,CAAC,CAkFlB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cleocode/core",
3
- "version": "2026.3.70",
3
+ "version": "2026.3.72",
4
4
  "description": "CLEO core business logic kernel — tasks, sessions, memory, orchestration, lifecycle, with bundled SQLite store",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -37,10 +37,10 @@
37
37
  "write-file-atomic": "^6.0.0",
38
38
  "yaml": "^2.8.2",
39
39
  "zod": "^3.25.76",
40
- "@cleocode/adapters": "2026.3.70",
41
- "@cleocode/agents": "2026.3.70",
42
- "@cleocode/contracts": "2026.3.70",
43
- "@cleocode/skills": "2026.3.70"
40
+ "@cleocode/adapters": "2026.3.72",
41
+ "@cleocode/agents": "2026.3.72",
42
+ "@cleocode/contracts": "2026.3.72",
43
+ "@cleocode/skills": "2026.3.72"
44
44
  },
45
45
  "engines": {
46
46
  "node": ">=24.0.0"
package/src/cleo.ts CHANGED
@@ -16,26 +16,26 @@
16
16
 
17
17
  import path from 'node:path';
18
18
  import type {
19
+ AdminAPI,
20
+ AgentsAPI,
21
+ CleoInitOptions,
19
22
  DataAccessor,
20
- ExternalTask,
21
- ExternalTaskLink,
22
- ReconcileOptions,
23
- ReconcileResult,
24
- Task,
25
- TaskPriority,
26
- TaskSize,
27
- TaskStatus,
28
- TaskType,
23
+ IntelligenceAPI,
24
+ LifecycleAPI,
25
+ MemoryAPI,
26
+ NexusAPI,
27
+ OrchestrationAPI,
28
+ ReleaseAPI,
29
+ SessionsAPI,
30
+ StickyAPI,
31
+ SyncAPI,
32
+ TasksAPI,
29
33
  } from '@cleocode/contracts';
30
34
  // Admin
31
35
  import { exportTasks } from './admin/export.js';
32
- import type { ImportParams } from './admin/import.js';
33
36
  import { importTasks } from './admin/import.js';
34
37
  // Agents
35
38
  import {
36
- type AgentCapacity,
37
- type AgentHealthStatus,
38
- type AgentInstanceRow,
39
39
  checkAgentHealth,
40
40
  deregisterAgent,
41
41
  detectCrashedAgents,
@@ -43,16 +43,10 @@ import {
43
43
  heartbeat,
44
44
  isOverloaded,
45
45
  listAgentInstances,
46
- type RegisterAgentOptions,
47
46
  registerAgent,
48
47
  } from './agents/index.js';
49
48
  // Intelligence
50
- import {
51
- type BlastRadius,
52
- calculateBlastRadius,
53
- type ImpactReport,
54
- predictImpact,
55
- } from './intelligence/index.js';
49
+ import { calculateBlastRadius, predictImpact } from './intelligence/index.js';
56
50
  // Lifecycle
57
51
  import {
58
52
  checkGate,
@@ -66,7 +60,6 @@ import {
66
60
  skipStage,
67
61
  startStage,
68
62
  } from './lifecycle/index.js';
69
- import type { BrainObservationType } from './memory/brain-retrieval.js';
70
63
  // Memory
71
64
  import {
72
65
  fetchBrainEntries,
@@ -74,7 +67,6 @@ import {
74
67
  searchBrainCompact,
75
68
  timelineBrain,
76
69
  } from './memory/brain-retrieval.js';
77
- import type { HybridSearchOptions } from './memory/brain-search.js';
78
70
  import { hybridSearch, searchBrain } from './memory/brain-search.js';
79
71
  // Nexus
80
72
  import { discoverRelated, searchAcrossProjects } from './nexus/discover.js';
@@ -137,6 +129,8 @@ import {
137
129
  startSession,
138
130
  suspendSession,
139
131
  } from './sessions/index.js';
132
+ // Session snapshots (Phase 3: persistence)
133
+ import { restoreSession, serializeSession } from './sessions/snapshot.js';
140
134
  // Sticky
141
135
  import {
142
136
  addSticky,
@@ -162,237 +156,24 @@ import { showTask } from './tasks/show.js';
162
156
  import { updateTask } from './tasks/update.js';
163
157
 
164
158
  // ============================================================================
165
- // Domain API interfaces
166
- // ============================================================================
167
-
168
- export interface TasksAPI {
169
- add(params: {
170
- title: string;
171
- description: string;
172
- parent?: string;
173
- priority?: TaskPriority;
174
- type?: TaskType;
175
- size?: TaskSize;
176
- phase?: string;
177
- labels?: string[];
178
- depends?: string[];
179
- notes?: string;
180
- }): Promise<unknown>;
181
- find(params: {
182
- query?: string;
183
- id?: string;
184
- status?: TaskStatus;
185
- limit?: number;
186
- }): Promise<unknown>;
187
- show(taskId: string): Promise<unknown>;
188
- list(params?: {
189
- status?: TaskStatus;
190
- priority?: TaskPriority;
191
- parentId?: string;
192
- phase?: string;
193
- limit?: number;
194
- }): Promise<unknown>;
195
- update(params: {
196
- taskId: string;
197
- title?: string;
198
- status?: TaskStatus;
199
- priority?: TaskPriority;
200
- description?: string;
201
- notes?: string;
202
- }): Promise<unknown>;
203
- complete(params: { taskId: string; notes?: string }): Promise<unknown>;
204
- delete(params: { taskId: string; force?: boolean }): Promise<unknown>;
205
- archive(params?: { before?: string; taskIds?: string[]; dryRun?: boolean }): Promise<unknown>;
206
- /** Start working on a specific task (sets focus). */
207
- start(taskId: string): Promise<unknown>;
208
- /** Stop working on the current task (clears focus). */
209
- stop(): Promise<{ previousTask: string | null }>;
210
- /** Get the current task work state. */
211
- current(): Promise<unknown>;
212
- }
213
-
214
- export interface SessionsAPI {
215
- start(params: {
216
- name: string;
217
- scope: string;
218
- agent?: string;
219
- startTask?: string;
220
- }): Promise<unknown>;
221
- end(params?: { note?: string }): Promise<unknown>;
222
- status(): Promise<unknown>;
223
- resume(sessionId: string): Promise<unknown>;
224
- list(params?: { status?: string; limit?: number }): Promise<unknown>;
225
- find(params?: {
226
- status?: string;
227
- scope?: string;
228
- query?: string;
229
- limit?: number;
230
- }): Promise<unknown>;
231
- show(sessionId: string): Promise<unknown>;
232
- suspend(sessionId: string, reason?: string): Promise<unknown>;
233
- briefing(params?: { maxNextTasks?: number; scope?: string }): Promise<unknown>;
234
- handoff(sessionId: string, options?: { note?: string; nextAction?: string }): Promise<unknown>;
235
- gc(maxAgeHours?: number): Promise<unknown>;
236
- recordDecision(params: {
237
- sessionId: string;
238
- taskId: string;
239
- decision: string;
240
- rationale: string;
241
- alternatives?: string[];
242
- }): Promise<unknown>;
243
- recordAssumption(params: {
244
- assumption: string;
245
- confidence: 'high' | 'medium' | 'low';
246
- sessionId?: string;
247
- taskId?: string;
248
- }): Promise<unknown>;
249
- contextDrift(params?: { sessionId?: string }): Promise<unknown>;
250
- decisionLog(params?: { sessionId?: string; taskId?: string }): Promise<unknown>;
251
- lastHandoff(scope?: { type: string; epicId?: string }): Promise<unknown>;
252
- }
253
-
254
- export interface MemoryAPI {
255
- observe(params: { text: string; title?: string; type?: BrainObservationType }): Promise<unknown>;
256
- find(params: {
257
- query: string;
258
- limit?: number;
259
- tables?: Array<'decisions' | 'patterns' | 'learnings' | 'observations'>;
260
- }): Promise<unknown>;
261
- fetch(params: { ids: string[] }): Promise<unknown>;
262
- timeline(params: { anchor: string; depthBefore?: number; depthAfter?: number }): Promise<unknown>;
263
- search(query: string, options?: { limit?: number }): Promise<unknown>;
264
- hybridSearch(query: string, options?: HybridSearchOptions): Promise<unknown>;
265
- }
266
-
267
- export interface OrchestrationAPI {
268
- start(epicId: string): Promise<unknown>;
269
- analyze(epicId: string): Promise<unknown>;
270
- readyTasks(epicId: string): Promise<unknown>;
271
- nextTask(epicId: string): Promise<unknown>;
272
- context(epicId: string): Promise<unknown>;
273
- dependencyGraph(tasks: Task[]): unknown;
274
- epicStatus(epicId: string, title: string, children: Task[]): unknown;
275
- progress(tasks: Task[]): unknown;
276
- }
277
-
278
- export interface LifecycleAPI {
279
- status(epicId: string): Promise<unknown>;
280
- startStage(epicId: string, stage: string): Promise<unknown>;
281
- completeStage(epicId: string, stage: string, artifacts?: string[]): Promise<unknown>;
282
- skipStage(epicId: string, stage: string, reason: string): Promise<unknown>;
283
- checkGate(epicId: string, targetStage: string): Promise<unknown>;
284
- history(epicId: string): Promise<unknown>;
285
- resetStage(epicId: string, stage: string, reason: string): Promise<unknown>;
286
- passGate(epicId: string, gateName: string, agent?: string): Promise<unknown>;
287
- failGate(epicId: string, gateName: string, reason?: string): Promise<unknown>;
288
- stages: readonly string[];
289
- }
290
-
291
- export interface ReleaseAPI {
292
- prepare(params: { version: string; tasks?: string[]; notes?: string }): Promise<unknown>;
293
- commit(params: { version: string }): Promise<unknown>;
294
- tag(params: { version: string }): Promise<unknown>;
295
- push(params: { version: string; remote?: string; explicitPush?: boolean }): Promise<unknown>;
296
- rollback(params: { version: string; reason?: string }): Promise<unknown>;
297
- calculateVersion(current: string, bumpType: string): string;
298
- bumpVersion(): Promise<unknown>;
299
- }
300
-
301
- export interface AdminAPI {
302
- export(params?: Record<string, unknown>): Promise<unknown>;
303
- import(params: Omit<ImportParams, 'cwd'>): Promise<unknown>;
304
- }
305
-
306
- export interface StickyAPI {
307
- add(params: {
308
- content: string;
309
- tags?: string[];
310
- priority?: string;
311
- color?: string;
312
- }): Promise<unknown>;
313
- show(stickyId: string): Promise<unknown>;
314
- list(params?: {
315
- status?: string;
316
- color?: string;
317
- priority?: string;
318
- limit?: number;
319
- }): Promise<unknown>;
320
- archive(stickyId: string): Promise<unknown>;
321
- purge(stickyId: string): Promise<unknown>;
322
- convert(params: {
323
- stickyId: string;
324
- targetType: 'task' | 'memory' | 'task_note' | 'session_note';
325
- title?: string;
326
- memoryType?: string;
327
- taskId?: string;
328
- }): Promise<unknown>;
329
- }
330
-
331
- export interface NexusAPI {
332
- init(): Promise<unknown>;
333
- register(params: { path: string; name?: string; permissions?: string }): Promise<unknown>;
334
- unregister(params: { name: string }): Promise<unknown>;
335
- list(): Promise<unknown>;
336
- show(params: { name: string }): Promise<unknown>;
337
- sync(params?: { name?: string }): Promise<unknown>;
338
- discover(params: { query: string; method?: string; limit?: number }): Promise<unknown>;
339
- search(params: { pattern: string; project?: string; limit?: number }): Promise<unknown>;
340
- setPermission(params: { name: string; level: 'read' | 'write' | 'execute' }): Promise<unknown>;
341
- sharingStatus(): Promise<unknown>;
342
- }
343
-
344
- export interface SyncAPI {
345
- /** Reconcile external tasks with CLEO as SSoT. */
346
- reconcile(params: {
347
- externalTasks: ExternalTask[];
348
- providerId: string;
349
- dryRun?: boolean;
350
- conflictPolicy?: ReconcileOptions['conflictPolicy'];
351
- defaultPhase?: string;
352
- defaultLabels?: string[];
353
- }): Promise<ReconcileResult>;
354
- /** Get all external task links for a provider. */
355
- getLinks(providerId: string): Promise<ExternalTaskLink[]>;
356
- /** Get all external task links for a CLEO task. */
357
- getTaskLinks(taskId: string): Promise<ExternalTaskLink[]>;
358
- /** Remove all external task links for a provider. */
359
- removeProviderLinks(providerId: string): Promise<number>;
360
- }
361
-
362
- export interface AgentsAPI {
363
- /** Register a new agent instance. */
364
- register(options: RegisterAgentOptions): Promise<AgentInstanceRow>;
365
- /** Deregister an agent instance. */
366
- deregister(agentId: string): Promise<AgentInstanceRow | null>;
367
- /** Get health status for a specific agent. */
368
- health(agentId: string): Promise<AgentHealthStatus | null>;
369
- /** Detect agents that have crashed (missed heartbeats). */
370
- detectCrashed(thresholdMs?: number): Promise<AgentInstanceRow[]>;
371
- /** Record a heartbeat for an agent. */
372
- recordHeartbeat(agentId: string): Promise<unknown>;
373
- /** Get capacity info for an agent. */
374
- capacity(agentId: string): Promise<AgentCapacity | null>;
375
- /** Check if system is overloaded (available capacity below threshold). */
376
- isOverloaded(threshold?: number): Promise<boolean>;
377
- /** List all agent instances with optional filters. */
378
- list(params?: { status?: string; agentType?: string }): Promise<AgentInstanceRow[]>;
379
- }
380
-
381
- export interface IntelligenceAPI {
382
- /** Predict impact of a change description on related tasks. */
383
- predictImpact(change: string): Promise<ImpactReport>;
384
- /** Calculate blast radius for a task change. */
385
- blastRadius(taskId: string): Promise<BlastRadius>;
386
- }
387
-
388
- // ============================================================================
389
- // Init options
159
+ // Domain API interfaces — re-exported from @cleocode/contracts/facade
390
160
  // ============================================================================
391
161
 
392
- export interface CleoInitOptions {
393
- store?: DataAccessor;
394
- caamp?: boolean;
395
- }
162
+ export type {
163
+ AdminAPI,
164
+ AgentsAPI,
165
+ CleoInitOptions,
166
+ IntelligenceAPI,
167
+ LifecycleAPI,
168
+ MemoryAPI,
169
+ NexusAPI,
170
+ OrchestrationAPI,
171
+ ReleaseAPI,
172
+ SessionsAPI,
173
+ StickyAPI,
174
+ SyncAPI,
175
+ TasksAPI,
176
+ } from '@cleocode/contracts';
396
177
 
397
178
  // ============================================================================
398
179
  // Cleo facade class
@@ -510,6 +291,16 @@ export class Cleo {
510
291
  contextDrift: (p) => getContextDrift(root, p),
511
292
  decisionLog: (p) => getDecisionLog(root, { sessionId: p?.sessionId, taskId: p?.taskId }),
512
293
  lastHandoff: (scope) => getLastHandoff(root, scope),
294
+ serialize: (p) =>
295
+ serializeSession(root, {
296
+ sessionId: p?.sessionId,
297
+ maxObservations: p?.maxObservations,
298
+ }),
299
+ restore: (snapshot, p) =>
300
+ restoreSession(root, snapshot as import('./sessions/snapshot.js').SessionSnapshot, {
301
+ agent: p?.agent,
302
+ activate: p?.activate,
303
+ }),
513
304
  };
514
305
  }
515
306
 
package/src/index.ts CHANGED
@@ -299,6 +299,16 @@ export {
299
299
  sessionStatus,
300
300
  startSession,
301
301
  } from './sessions/index.js';
302
+ export type {
303
+ RestoreOptions,
304
+ SerializeOptions,
305
+ SessionSnapshot,
306
+ SnapshotDecision,
307
+ SnapshotObservation,
308
+ SnapshotTaskContext,
309
+ } from './sessions/snapshot.js';
310
+ // Session snapshots (Phase 3: persistence)
311
+ export { restoreSession, serializeSession } from './sessions/snapshot.js';
302
312
  export { getMigrationStatus as getSystemMigrationStatus } from './system/migrate.js';
303
313
  export { checkStorageMigration } from './system/storage-preflight.js';
304
314
  // Task work