Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions scilink/agents/meta_agent/meta_orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -1344,8 +1344,32 @@ def _auto_checkpoint(self):
with open(self.checkpoint_path, 'w') as f:
json.dump(checkpoint_data, f, indent=2, default=str)
print(f" ✅ Auto-checkpoint saved")
return True
except Exception as e:
logging.warning(f"Auto-checkpoint failed: {e}")
return False

def save_checkpoint(self) -> str:
"""Save the meta checkpoint on demand and reset the auto-save timer.

Children checkpoint themselves during delegations; this persists the
meta-level state (mode, message count, delegation ledger) so the
session can be resumed without waiting for the periodic auto-save.

Returns:
The checkpoint file path.

Raises:
RuntimeError: if the checkpoint could not be written.
"""
if not self._auto_checkpoint():
raise RuntimeError(
f"Failed to write checkpoint to {self.checkpoint_path} "
"(see log for details)."
)
self.last_checkpoint_message_count = self.message_count
self._save_history()
return str(self.checkpoint_path)

def _trim_history(self, history: List[Dict], max_messages: int = None) -> List[Dict]:
"""Keep only recent messages to avoid context window overflow."""
Expand Down
27 changes: 27 additions & 0 deletions scilink/agents/meta_agent/meta_orchestrator_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,33 @@ def get_delegation_history(limit: int = None) -> str:
required=[],
)

# -- save_checkpoint -------------------------------------------------
def save_checkpoint() -> str:
print(" 💾 Tool: Saving meta checkpoint...")
try:
path = self.orch.save_checkpoint()
return json.dumps({
"status": "success",
"checkpoint_path": path,
"delegations_saved": len(self.orch._delegation_ledger),
})
except Exception as e:
return json.dumps({"status": "error", "message": str(e)})

self._register_tool(
func=save_checkpoint,
name="save_checkpoint",
description=(
"Save the meta session state (mode, delegation ledger, chat "
"history) to checkpoint.json so the session can be resumed "
"later. The state is also auto-saved periodically; call this "
"when the user asks to save/checkpoint the session, or before "
"they intend to stop."
),
parameters={},
required=[],
)

# -- review_distilled_skills ----------------------------------------
def review_distilled_skills(action: str = "list", skill: str = None,
to_domain: str = None, staged: str = None,
Expand Down
20 changes: 18 additions & 2 deletions scilink/cli/meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,8 +544,9 @@ def print_help(self):
print(" /skill <path> Register a custom skill (.md), shared with specialists")
print(" /tool <path> Register a custom tool file (.py), shared with specialists")
print(" /mcp <config> Connect an MCP server, shared with specialists")
print(" /checkpoint Save session state now (also auto-saved, and saved on exit)")
print(" /clear Clear screen")
print(" /quit or /exit Exit")
print(" /quit or /exit Exit (saves a checkpoint)")
print("\nOr just describe your research goal and let the meta-agent route it!")
print("=" * 60)

Expand Down Expand Up @@ -637,6 +638,10 @@ def handle_command(self, user_input: str):
self._connect_mcp_servers([parts[1].strip()])
return True

if cmd == "/checkpoint":
self._save_checkpoint()
return True

if cmd == "/clear":
os.system('cls' if os.name == 'nt' else 'clear')
print("🧭 Meta-Agent - Session Resumed\n")
Expand All @@ -647,6 +652,14 @@ def handle_command(self, user_input: str):

return False

def _save_checkpoint(self):
"""Save the meta checkpoint, reporting rather than raising on failure."""
try:
path = self.agent.save_checkpoint()
print(f"\n💾 Checkpoint saved: {path}")
except Exception as e:
print(f"\n❌ Checkpoint failed: {e}")

def run(self):
"""Main interactive loop."""
self.setup()
Expand All @@ -671,14 +684,17 @@ def run(self):
try:
user_input = input("You: ").strip()
except (EOFError, KeyboardInterrupt):
print("\n\n👋 Goodbye!")
print()
self._save_checkpoint()
print("\n👋 Goodbye!")
break

if not user_input:
continue

handled = self.handle_command(user_input)
if handled == "QUIT":
self._save_checkpoint()
print("\n👋 Goodbye!")
break
if handled is True:
Expand Down
Loading