@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/mcp/index.js CHANGED
@@ -67855,6 +67855,162 @@ var init_bootstrap = __esm({
67855
67855
  }
67856
67856
  });
67857
67857
 
67858
+ // packages/core/src/sessions/snapshot.ts
67859
+ async function serializeSession(projectRoot, options = {}, accessor) {
67860
+ const acc = accessor ?? await getAccessor(projectRoot);
67861
+ const maxObs = options.maxObservations ?? 10;
67862
+ const maxDescLen = options.maxDescriptionLength ?? 500;
67863
+ const sessions2 = await acc.loadSessions();
67864
+ let session;
67865
+ if (options.sessionId) {
67866
+ session = sessions2.find((s) => s.id === options.sessionId);
67867
+ } else {
67868
+ session = sessions2.filter((s) => s.status === "active").sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime())[0];
67869
+ }
67870
+ if (!session) {
67871
+ throw new CleoError(
67872
+ 31 /* SESSION_NOT_FOUND */,
67873
+ options.sessionId ? `Session '${options.sessionId}' not found` : "No active session to serialize",
67874
+ { fix: "Use 'cleo session list' to see available sessions" }
67875
+ );
67876
+ }
67877
+ const handoff = await computeHandoff(projectRoot, { sessionId: session.id });
67878
+ const decisionLog = await getDecisionLog(projectRoot, { sessionId: session.id });
67879
+ const decisions = decisionLog.map((d) => ({
67880
+ decision: d.decision,
67881
+ rationale: d.rationale,
67882
+ taskId: d.taskId,
67883
+ recordedAt: d.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
67884
+ }));
67885
+ let observations = [];
67886
+ try {
67887
+ const { searchBrainCompact: searchBrainCompact2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
67888
+ const results = await searchBrainCompact2(projectRoot, {
67889
+ query: session.id,
67890
+ limit: maxObs,
67891
+ tables: ["observations"]
67892
+ });
67893
+ if (Array.isArray(results)) {
67894
+ observations = results.map(
67895
+ (r) => ({
67896
+ id: r.id,
67897
+ text: r.text ?? "",
67898
+ type: r.type ?? "discovery",
67899
+ createdAt: r.createdAt ?? ""
67900
+ })
67901
+ );
67902
+ }
67903
+ } catch {
67904
+ }
67905
+ let activeTask = null;
67906
+ if (session.taskWork?.taskId) {
67907
+ try {
67908
+ const { tasks: tasks2 } = await acc.queryTasks({});
67909
+ const task = tasks2.find((t) => t.id === session.taskWork?.taskId);
67910
+ if (task) {
67911
+ const desc7 = task.description ?? "";
67912
+ activeTask = {
67913
+ taskId: task.id,
67914
+ title: task.title,
67915
+ status: task.status,
67916
+ priority: task.priority ?? "medium",
67917
+ description: desc7.length > maxDescLen ? desc7.slice(0, maxDescLen) + "..." : desc7,
67918
+ acceptance: Array.isArray(task.acceptance) ? task.acceptance.join("\n") : task.acceptance ?? void 0
67919
+ };
67920
+ }
67921
+ } catch {
67922
+ }
67923
+ }
67924
+ const startTime = new Date(session.startedAt).getTime();
67925
+ const now2 = Date.now();
67926
+ const durationMinutes = Math.round((now2 - startTime) / 6e4);
67927
+ return {
67928
+ version: SNAPSHOT_VERSION,
67929
+ capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
67930
+ session,
67931
+ handoff,
67932
+ decisions,
67933
+ observations,
67934
+ activeTask,
67935
+ durationMinutes
67936
+ };
67937
+ }
67938
+ async function restoreSession(projectRoot, snapshot, options = {}, accessor) {
67939
+ if (snapshot.version > SNAPSHOT_VERSION) {
67940
+ throw new CleoError(
67941
+ 6 /* VALIDATION_ERROR */,
67942
+ `Snapshot version ${snapshot.version} is newer than supported version ${SNAPSHOT_VERSION}`,
67943
+ { fix: "Upgrade @cleocode/core to a newer version that supports this snapshot format" }
67944
+ );
67945
+ }
67946
+ const acc = accessor ?? await getAccessor(projectRoot);
67947
+ const activate = options.activate ?? true;
67948
+ if (activate) {
67949
+ const sessions2 = await acc.loadSessions();
67950
+ const scope = snapshot.session.scope;
67951
+ const activeConflict = sessions2.find(
67952
+ (s) => s.status === "active" && s.scope.type === scope.type && s.scope.epicId === scope.epicId && s.id !== snapshot.session.id
67953
+ );
67954
+ if (activeConflict) {
67955
+ throw new CleoError(
67956
+ 32 /* SCOPE_CONFLICT */,
67957
+ `Active session '${activeConflict.id}' already exists for scope ${scope.type}${scope.epicId ? ":" + scope.epicId : ""}`,
67958
+ {
67959
+ fix: `End the active session first with 'cleo session end' or restore without activating`,
67960
+ alternatives: [
67961
+ { action: "End conflicting session", command: "cleo session end" },
67962
+ { action: "Restore without activating", command: "Restore with activate: false" }
67963
+ ]
67964
+ }
67965
+ );
67966
+ }
67967
+ }
67968
+ const restoredSession = {
67969
+ ...snapshot.session,
67970
+ status: activate ? "active" : snapshot.session.status,
67971
+ notes: [
67972
+ ...snapshot.session.notes ?? [],
67973
+ `Restored from snapshot at ${(/* @__PURE__ */ new Date()).toISOString()} (captured ${snapshot.capturedAt}, duration ${snapshot.durationMinutes}m)`
67974
+ ],
67975
+ resumeCount: (snapshot.session.resumeCount ?? 0) + 1
67976
+ };
67977
+ if (options.agent) {
67978
+ restoredSession.agent = options.agent;
67979
+ restoredSession.notes = [
67980
+ ...restoredSession.notes ?? [],
67981
+ `Agent handoff: ${snapshot.session.agent ?? "unknown"} \u2192 ${options.agent}`
67982
+ ];
67983
+ }
67984
+ restoredSession.handoffJson = JSON.stringify(snapshot.handoff);
67985
+ await acc.upsertSingleSession(restoredSession);
67986
+ try {
67987
+ const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
67988
+ await hooks2.dispatch("onSessionStart", projectRoot, {
67989
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
67990
+ sessionId: restoredSession.id,
67991
+ name: restoredSession.name,
67992
+ scope: restoredSession.scope,
67993
+ agent: restoredSession.agent,
67994
+ restored: true,
67995
+ snapshotCapturedAt: snapshot.capturedAt
67996
+ });
67997
+ } catch {
67998
+ }
67999
+ return restoredSession;
68000
+ }
68001
+ var SNAPSHOT_VERSION;
68002
+ var init_snapshot2 = __esm({
68003
+ "packages/core/src/sessions/snapshot.ts"() {
68004
+ "use strict";
68005
+ init_src();
68006
+ init_errors3();
68007
+ init_data_accessor();
68008
+ init_decisions();
68009
+ init_handoff();
68010
+ SNAPSHOT_VERSION = 1;
68011
+ }
68012
+ });
68013
+
67858
68014
  // packages/core/src/cleo.ts
67859
68015
  import path from "node:path";
67860
68016
  var Cleo;
@@ -67877,6 +68033,7 @@ var init_cleo = __esm({
67877
68033
  init_link_store();
67878
68034
  init_release2();
67879
68035
  init_sessions();
68036
+ init_snapshot2();
67880
68037
  init_sticky();
67881
68038
  init_data_accessor();
67882
68039
  init_task_work();
@@ -67986,7 +68143,15 @@ var init_cleo = __esm({
67986
68143
  recordAssumption: (p) => recordAssumption(root, p),
67987
68144
  contextDrift: (p) => getContextDrift(root, p),
67988
68145
  decisionLog: (p) => getDecisionLog(root, { sessionId: p?.sessionId, taskId: p?.taskId }),
67989
- lastHandoff: (scope) => getLastHandoff(root, scope)
68146
+ lastHandoff: (scope) => getLastHandoff(root, scope),
68147
+ serialize: (p) => serializeSession(root, {
68148
+ sessionId: p?.sessionId,
68149
+ maxObservations: p?.maxObservations
68150
+ }),
68151
+ restore: (snapshot, p) => restoreSession(root, snapshot, {
68152
+ agent: p?.agent,
68153
+ activate: p?.activate
68154
+ })
67990
68155
  };
67991
68156
  }
67992
68157
  // === Memory ===
@@ -68235,6 +68400,7 @@ var init_src2 = __esm({
68235
68400
  init_migration();
68236
68401
  init_reconciliation();
68237
68402
  init_sessions();
68403
+ init_snapshot2();
68238
68404
  init_migrate();
68239
68405
  init_storage_preflight();
68240
68406
  init_task_work();
@@ -79483,6 +79649,7 @@ __export(internal_exports, {
79483
79649
  resolveTask: () => resolveTask,
79484
79650
  restoreBackup: () => restoreBackup,
79485
79651
  restoreFromBackup: () => restoreFromBackup,
79652
+ restoreSession: () => restoreSession,
79486
79653
  resumeSession: () => resumeSession,
79487
79654
  roadmap: () => roadmap_exports,
79488
79655
  rollbackRelease: () => rollbackRelease,
@@ -79510,6 +79677,7 @@ __export(internal_exports, {
79510
79677
  selectSessionSchema: () => selectSessionSchema,
79511
79678
  selectTaskSchema: () => selectTaskSchema,
79512
79679
  sequence: () => sequence_exports,
79680
+ serializeSession: () => serializeSession,
79513
79681
  sessionStatus: () => sessionStatus,
79514
79682
  sessionStatusSchema: () => sessionStatusSchema,
79515
79683
  sessions: () => sessions_exports,