Skip to content
Draft
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
5 changes: 3 additions & 2 deletions src/praisonai-agents/praisonaiagents/agent/memory_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,9 @@ def _init_session_store(self):
return

try:
from ..session import get_default_session_store
self._session_store = get_default_session_store()
if self._session_store is None:
from ..session import get_default_session_store
self._session_store = get_default_session_store()

# Restore chat history from previous session
history = self._session_store.get_chat_history(self._session_id)
Expand Down
83 changes: 38 additions & 45 deletions src/praisonai/praisonai/cli/commands/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ class Args:
args = Args()
args.auto_save = auto_save_name
args.resume_session = session_id
args.cli_project_sessions = bool(session_id or auto_save_name)

praison.args = args

Expand Down Expand Up @@ -349,10 +350,9 @@ def _run_prompt(
if not no_save:
import uuid
auto_save_name = session_id or "session-" + str(uuid.uuid4())[:8]

# If output_mode is "actions", use direct Agent with actions preset
if output_mode == "actions":
from praisonaiagents import Agent
from ..state.project_sessions import build_cli_memory_config, apply_cli_session_continuity

agent_config = {
"name": "RunAgent",
Expand All @@ -370,13 +370,13 @@ def _run_prompt(
approval, all_tools=approve_all_tools, timeout=approval_timeout,
)

# Add session support to Agent if needed
if session_id:
agent_config["resume_session"] = session_id
if auto_save_name:
agent_config["auto_save"] = auto_save_name
memory_cfg = build_cli_memory_config(session_id, auto_save_name)
if memory_cfg is not None:
agent_config["memory"] = memory_cfg

agent = Agent(**agent_config)
if session_id or auto_save_name:
apply_cli_session_continuity(agent, session_id or auto_save_name, auto_save=auto_save_name)
result = agent.start(prompt)

output.emit_result(
Expand Down Expand Up @@ -413,6 +413,7 @@ class Args:
args.auto_save = auto_save_name
args.history = None
args.resume_session = session_id
args.cli_project_sessions = bool(session_id or auto_save_name)
args.include_rules = None if no_rules else "auto"
args.no_rules = no_rules
args.workflow = None
Expand Down Expand Up @@ -440,44 +441,6 @@ class Args:

praison.args = args

# If output_mode is "actions", use direct Agent with actions preset
if output_mode == "actions":
from praisonaiagents import Agent

agent_config = {
"name": "RunAgent",
"role": "Assistant",
"goal": "Complete the task",
"output": "actions", # Use actions preset
}
if model:
agent_config["llm"] = model

# Resolve approval backend if specified
if approval:
from praisonai.cli.features.approval import resolve_approval_config
agent_config["approval"] = resolve_approval_config(
approval, all_tools=approve_all_tools, timeout=approval_timeout,
)

# Add session support to Agent if needed
if session_id:
agent_config["resume_session"] = session_id
if auto_save_name:
agent_config["auto_save"] = auto_save_name

agent = Agent(**agent_config)
result = agent.start(prompt)

output.emit_result(
message="Prompt completed",
data={"result": str(result) if result else None}
)

# Don't print result again - actions mode already shows output
return

# Use handle_direct_prompt for other modes
result = praison.handle_direct_prompt(prompt)

output.emit_result(
Expand Down Expand Up @@ -536,6 +499,22 @@ def _run_from_file_profiled(
)
if model:
praison.config_list[0]['model'] = model

# Apply session continuity if requested
session_id, auto_save_name = resolve_session_params(
continue_session, session, fork, no_save
)
if session_id or auto_save_name:
class Args:
pass

args = Args()
args.auto_save = auto_save_name
args.resume_session = session_id
args.cli_project_sessions = bool(session_id or auto_save_name)

praison.args = args

profiler.mark_init_end()

# Execution phase
Expand Down Expand Up @@ -597,7 +576,21 @@ def _run_prompt_profiled(
if model:
agent_config["llm"] = model

# Apply session continuity if requested
session_id, auto_save_name = resolve_session_params(
continue_session, session, fork, no_save
)
if session_id or auto_save_name:
from ..state.project_sessions import build_cli_memory_config, apply_cli_session_continuity

memory_cfg = build_cli_memory_config(session_id, auto_save_name)
if memory_cfg is not None:
agent_config["memory"] = memory_cfg

agent = Agent(**agent_config)
if session_id or auto_save_name:
apply_cli_session_continuity(agent, session_id or auto_save_name, auto_save=auto_save_name)

profiler.mark_init_end()

# Execution phase
Expand Down
32 changes: 32 additions & 0 deletions src/praisonai/praisonai/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,13 +362,20 @@ def main(self):
original_agent_file = self.agent_file

# Parse args - this returns both args and unknown_args
preserved_args = getattr(self, 'args', None)
parse_result = self.parse_args()
if isinstance(parse_result, tuple):
args, unknown_args = parse_result
else:
args = parse_result
unknown_args = []

# Preserve project session flags set by ``praison run`` before parse_args()
if preserved_args and getattr(preserved_args, 'cli_project_sessions', False):
for attr in ('auto_save', 'resume_session', 'cli_project_sessions'):
if hasattr(preserved_args, attr):
setattr(args, attr, getattr(preserved_args, attr))

# Store args for use in handle_direct_prompt
self.args = args
invocation_cmd = "praisonai"
Expand Down Expand Up @@ -4243,6 +4250,15 @@ def _extract_cli_config_for_yaml(self):
handoff_detect_cycles = getattr(self.args, 'handoff_detect_cycles', None)
if handoff_detect_cycles is not None:
cli_config['handoff_detect_cycles'] = handoff_detect_cycles

if getattr(self.args, 'cli_project_sessions', False):
from .state.project_sessions import build_cli_memory_config
memory_cfg = build_cli_memory_config(
getattr(self.args, 'resume_session', None),
getattr(self.args, 'auto_save', None),
)
if memory_cfg is not None:
cli_config['memory'] = memory_cfg

return cli_config

Expand Down Expand Up @@ -4509,6 +4525,15 @@ def handle_direct_prompt(self, prompt):
else:
agent_config["memory"] = True
print("[bold cyan]Memory enabled - agent will remember context across sessions[/bold cyan]")
elif getattr(self.args, 'cli_project_sessions', False) and (
getattr(self.args, 'resume_session', None) or getattr(self.args, 'auto_save', None)
):
from .state.project_sessions import build_cli_memory_config
agent_config["memory"] = build_cli_memory_config(
getattr(self.args, 'resume_session', None),
getattr(self.args, 'auto_save', None),
)
print(f"[bold cyan]Project session enabled - session '{agent_config['memory'].auto_save}'[/bold cyan]")
elif getattr(self.args, 'auto_save', None):
from praisonaiagents import MemoryConfig
agent_config["memory"] = MemoryConfig(auto_save=self.args.auto_save)
Expand Down Expand Up @@ -4792,6 +4817,13 @@ def level_based_approve(function_name, arguments, risk_level):
flow.display_workflow_start("Direct Prompt", ["DirectAgent"])

agent = PraisonAgent(**agent_config)

if hasattr(self, 'args') and getattr(self.args, 'cli_project_sessions', False):
session_id = getattr(self.args, 'resume_session', None) or getattr(self.args, 'auto_save', None)
auto_save = getattr(self.args, 'auto_save', None)
if session_id:
from .state.project_sessions import apply_cli_session_continuity
apply_cli_session_continuity(agent, session_id, auto_save=auto_save)

# AutoRag - Automatic RAG retrieval decision
if hasattr(self, 'args') and getattr(self.args, 'auto_rag', False):
Expand Down
38 changes: 37 additions & 1 deletion src/praisonai/praisonai/cli/state/project_sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,40 @@ def find_last_session(project_path: Optional[str] = None) -> Optional[str]:
Session ID or None if no sessions exist
"""
store = get_project_session_store(project_path)
return store.get_last_session_id()
return store.get_last_session_id()


def build_cli_memory_config(
session_id: Optional[str] = None,
auto_save: Optional[str] = None,
):
"""Build MemoryConfig for ``praison run`` project-scoped session continuity."""
if not session_id and not auto_save:
return None

from praisonaiagents import MemoryConfig

sid = session_id or auto_save
return MemoryConfig(session_id=sid, auto_save=auto_save, history=True)


def apply_cli_session_continuity(agent, session_id: str, project_path: Optional[str] = None, auto_save: Optional[str] = None) -> None:
"""Wire an agent to the project session store and restore prior history."""
store = get_project_session_store(project_path)
agent._session_store = store
agent._session_id = session_id
agent._history_enabled = True
agent._history_session_id = session_id
if auto_save is not None:
agent.auto_save = auto_save

history = store.get_chat_history(session_id)
if history and not agent.chat_history:
for msg in history:
agent.chat_history.append({
"role": msg["role"],
"content": msg["content"],
})
agent._auto_save_last_index = len(agent.chat_history)

agent._session_store_initialized = True
57 changes: 57 additions & 0 deletions src/praisonai/tests/unit/cli/test_project_session_continuity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""Tests for praison run project-scoped session continuity."""

from praisonai.cli.state.project_sessions import (
apply_cli_session_continuity,
build_cli_memory_config,
get_project_session_store,
)
from praisonaiagents import Agent


def test_build_cli_memory_config_enables_history():
cfg = build_cli_memory_config(session_id="sess-1", auto_save="sess-1")
assert cfg is not None
assert cfg.session_id == "sess-1"
assert cfg.auto_save == "sess-1"
assert cfg.history is True


def test_apply_cli_session_continuity_restores_project_history():
store = get_project_session_store()
session_id = "continuity-unit-test"
store.clear_session(session_id)
store.add_user_message(session_id, "remember this")
store.add_assistant_message(session_id, "acknowledged")

agent = Agent(name="RunAgent", memory=build_cli_memory_config(session_id, session_id))
apply_cli_session_continuity(agent, session_id)

assert agent._session_store.session_dir == store.session_dir
assert agent._session_id == session_id
assert len(agent.chat_history) == 2
assert agent.chat_history[0]["content"] == "remember this"

agent.chat_history.append({"role": "user", "content": "follow-up"})
agent.chat_history.append({"role": "assistant", "content": "reply"})
agent._auto_save_session()

saved = store.get_chat_history(session_id)
assert len(saved) == 4
assert saved[-1]["content"] == "reply"

store.clear_session(session_id)


def test_injected_session_store_not_overwritten():
store = get_project_session_store()
session_id = "store-injection-test"
store.clear_session(session_id)

agent = Agent(name="RunAgent", memory=build_cli_memory_config(session_id, session_id))
apply_cli_session_continuity(agent, session_id)
injected_dir = agent._session_store.session_dir

agent._init_session_store()
assert agent._session_store.session_dir == injected_dir

store.clear_session(session_id)
Loading