@cleocode/cleo 2026.3.71 → 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.
package/dist/cli/index.js CHANGED
@@ -67884,6 +67884,162 @@ var init_bootstrap = __esm({
67884
67884
  }
67885
67885
  });
67886
67886
 
67887
+ // packages/core/src/sessions/snapshot.ts
67888
+ async function serializeSession(projectRoot, options = {}, accessor) {
67889
+ const acc = accessor ?? await getAccessor(projectRoot);
67890
+ const maxObs = options.maxObservations ?? 10;
67891
+ const maxDescLen = options.maxDescriptionLength ?? 500;
67892
+ const sessions2 = await acc.loadSessions();
67893
+ let session;
67894
+ if (options.sessionId) {
67895
+ session = sessions2.find((s) => s.id === options.sessionId);
67896
+ } else {
67897
+ session = sessions2.filter((s) => s.status === "active").sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime())[0];
67898
+ }
67899
+ if (!session) {
67900
+ throw new CleoError(
67901
+ 31 /* SESSION_NOT_FOUND */,
67902
+ options.sessionId ? `Session '${options.sessionId}' not found` : "No active session to serialize",
67903
+ { fix: "Use 'cleo session list' to see available sessions" }
67904
+ );
67905
+ }
67906
+ const handoff = await computeHandoff(projectRoot, { sessionId: session.id });
67907
+ const decisionLog = await getDecisionLog(projectRoot, { sessionId: session.id });
67908
+ const decisions = decisionLog.map((d) => ({
67909
+ decision: d.decision,
67910
+ rationale: d.rationale,
67911
+ taskId: d.taskId,
67912
+ recordedAt: d.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
67913
+ }));
67914
+ let observations = [];
67915
+ try {
67916
+ const { searchBrainCompact: searchBrainCompact2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
67917
+ const results = await searchBrainCompact2(projectRoot, {
67918
+ query: session.id,
67919
+ limit: maxObs,
67920
+ tables: ["observations"]
67921
+ });
67922
+ if (Array.isArray(results)) {
67923
+ observations = results.map(
67924
+ (r) => ({
67925
+ id: r.id,
67926
+ text: r.text ?? "",
67927
+ type: r.type ?? "discovery",
67928
+ createdAt: r.createdAt ?? ""
67929
+ })
67930
+ );
67931
+ }
67932
+ } catch {
67933
+ }
67934
+ let activeTask = null;
67935
+ if (session.taskWork?.taskId) {
67936
+ try {
67937
+ const { tasks: tasks2 } = await acc.queryTasks({});
67938
+ const task = tasks2.find((t) => t.id === session.taskWork?.taskId);
67939
+ if (task) {
67940
+ const desc7 = task.description ?? "";
67941
+ activeTask = {
67942
+ taskId: task.id,
67943
+ title: task.title,
67944
+ status: task.status,
67945
+ priority: task.priority ?? "medium",
67946
+ description: desc7.length > maxDescLen ? desc7.slice(0, maxDescLen) + "..." : desc7,
67947
+ acceptance: Array.isArray(task.acceptance) ? task.acceptance.join("\n") : task.acceptance ?? void 0
67948
+ };
67949
+ }
67950
+ } catch {
67951
+ }
67952
+ }
67953
+ const startTime = new Date(session.startedAt).getTime();
67954
+ const now2 = Date.now();
67955
+ const durationMinutes = Math.round((now2 - startTime) / 6e4);
67956
+ return {
67957
+ version: SNAPSHOT_VERSION,
67958
+ capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
67959
+ session,
67960
+ handoff,
67961
+ decisions,
67962
+ observations,
67963
+ activeTask,
67964
+ durationMinutes
67965
+ };
67966
+ }
67967
+ async function restoreSession(projectRoot, snapshot, options = {}, accessor) {
67968
+ if (snapshot.version > SNAPSHOT_VERSION) {
67969
+ throw new CleoError(
67970
+ 6 /* VALIDATION_ERROR */,
67971
+ `Snapshot version ${snapshot.version} is newer than supported version ${SNAPSHOT_VERSION}`,
67972
+ { fix: "Upgrade @cleocode/core to a newer version that supports this snapshot format" }
67973
+ );
67974
+ }
67975
+ const acc = accessor ?? await getAccessor(projectRoot);
67976
+ const activate = options.activate ?? true;
67977
+ if (activate) {
67978
+ const sessions2 = await acc.loadSessions();
67979
+ const scope = snapshot.session.scope;
67980
+ const activeConflict = sessions2.find(
67981
+ (s) => s.status === "active" && s.scope.type === scope.type && s.scope.epicId === scope.epicId && s.id !== snapshot.session.id
67982
+ );
67983
+ if (activeConflict) {
67984
+ throw new CleoError(
67985
+ 32 /* SCOPE_CONFLICT */,
67986
+ `Active session '${activeConflict.id}' already exists for scope ${scope.type}${scope.epicId ? ":" + scope.epicId : ""}`,
67987
+ {
67988
+ fix: `End the active session first with 'cleo session end' or restore without activating`,
67989
+ alternatives: [
67990
+ { action: "End conflicting session", command: "cleo session end" },
67991
+ { action: "Restore without activating", command: "Restore with activate: false" }
67992
+ ]
67993
+ }
67994
+ );
67995
+ }
67996
+ }
67997
+ const restoredSession = {
67998
+ ...snapshot.session,
67999
+ status: activate ? "active" : snapshot.session.status,
68000
+ notes: [
68001
+ ...snapshot.session.notes ?? [],
68002
+ `Restored from snapshot at ${(/* @__PURE__ */ new Date()).toISOString()} (captured ${snapshot.capturedAt}, duration ${snapshot.durationMinutes}m)`
68003
+ ],
68004
+ resumeCount: (snapshot.session.resumeCount ?? 0) + 1
68005
+ };
68006
+ if (options.agent) {
68007
+ restoredSession.agent = options.agent;
68008
+ restoredSession.notes = [
68009
+ ...restoredSession.notes ?? [],
68010
+ `Agent handoff: ${snapshot.session.agent ?? "unknown"} \u2192 ${options.agent}`
68011
+ ];
68012
+ }
68013
+ restoredSession.handoffJson = JSON.stringify(snapshot.handoff);
68014
+ await acc.upsertSingleSession(restoredSession);
68015
+ try {
68016
+ const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
68017
+ await hooks2.dispatch("onSessionStart", projectRoot, {
68018
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
68019
+ sessionId: restoredSession.id,
68020
+ name: restoredSession.name,
68021
+ scope: restoredSession.scope,
68022
+ agent: restoredSession.agent,
68023
+ restored: true,
68024
+ snapshotCapturedAt: snapshot.capturedAt
68025
+ });
68026
+ } catch {
68027
+ }
68028
+ return restoredSession;
68029
+ }
68030
+ var SNAPSHOT_VERSION;
68031
+ var init_snapshot2 = __esm({
68032
+ "packages/core/src/sessions/snapshot.ts"() {
68033
+ "use strict";
68034
+ init_src();
68035
+ init_errors3();
68036
+ init_data_accessor();
68037
+ init_decisions();
68038
+ init_handoff();
68039
+ SNAPSHOT_VERSION = 1;
68040
+ }
68041
+ });
68042
+
67887
68043
  // packages/core/src/cleo.ts
67888
68044
  import path from "node:path";
67889
68045
  var Cleo;
@@ -67906,6 +68062,7 @@ var init_cleo = __esm({
67906
68062
  init_link_store();
67907
68063
  init_release2();
67908
68064
  init_sessions();
68065
+ init_snapshot2();
67909
68066
  init_sticky();
67910
68067
  init_data_accessor();
67911
68068
  init_task_work();
@@ -68015,7 +68172,15 @@ var init_cleo = __esm({
68015
68172
  recordAssumption: (p) => recordAssumption(root, p),
68016
68173
  contextDrift: (p) => getContextDrift(root, p),
68017
68174
  decisionLog: (p) => getDecisionLog(root, { sessionId: p?.sessionId, taskId: p?.taskId }),
68018
- lastHandoff: (scope) => getLastHandoff(root, scope)
68175
+ lastHandoff: (scope) => getLastHandoff(root, scope),
68176
+ serialize: (p) => serializeSession(root, {
68177
+ sessionId: p?.sessionId,
68178
+ maxObservations: p?.maxObservations
68179
+ }),
68180
+ restore: (snapshot, p) => restoreSession(root, snapshot, {
68181
+ agent: p?.agent,
68182
+ activate: p?.activate
68183
+ })
68019
68184
  };
68020
68185
  }
68021
68186
  // === Memory ===
@@ -68371,6 +68536,7 @@ __export(src_exports, {
68371
68536
  remote: () => remote_exports,
68372
68537
  research: () => research_exports,
68373
68538
  resolveProjectPath: () => resolveProjectPath,
68539
+ restoreSession: () => restoreSession,
68374
68540
  resumeSession: () => resumeSession,
68375
68541
  roadmap: () => roadmap_exports,
68376
68542
  routing: () => routing_exports,
@@ -68387,6 +68553,7 @@ __export(src_exports, {
68387
68553
  selectSessionSchema: () => selectSessionSchema,
68388
68554
  selectTaskSchema: () => selectTaskSchema,
68389
68555
  sequence: () => sequence_exports,
68556
+ serializeSession: () => serializeSession,
68390
68557
  sessionStatus: () => sessionStatus,
68391
68558
  sessionStatusSchema: () => sessionStatusSchema,
68392
68559
  sessions: () => sessions_exports,
@@ -68497,6 +68664,7 @@ var init_src2 = __esm({
68497
68664
  init_migration();
68498
68665
  init_reconciliation();
68499
68666
  init_sessions();
68667
+ init_snapshot2();
68500
68668
  init_migrate();
68501
68669
  init_storage_preflight();
68502
68670
  init_task_work();
@@ -79745,6 +79913,7 @@ __export(internal_exports, {
79745
79913
  resolveTask: () => resolveTask,
79746
79914
  restoreBackup: () => restoreBackup,
79747
79915
  restoreFromBackup: () => restoreFromBackup,
79916
+ restoreSession: () => restoreSession,
79748
79917
  resumeSession: () => resumeSession,
79749
79918
  roadmap: () => roadmap_exports,
79750
79919
  rollbackRelease: () => rollbackRelease,
@@ -79772,6 +79941,7 @@ __export(internal_exports, {
79772
79941
  selectSessionSchema: () => selectSessionSchema,
79773
79942
  selectTaskSchema: () => selectTaskSchema,
79774
79943
  sequence: () => sequence_exports,
79944
+ serializeSession: () => serializeSession,
79775
79945
  sessionStatus: () => sessionStatus,
79776
79946
  sessionStatusSchema: () => sessionStatusSchema,
79777
79947
  sessions: () => sessions_exports,