Skip to content

Commit e671898

Browse files
fix(cli): wire praison run session continuity to project session store
The CLI session continuity feature (#1929) discovered sessions in the project-scoped store but agents saved/loaded from the global default store. auto_save alone did not set session_id or enable history, so prior messages were never restored and new messages were silently lost. - Add build_cli_memory_config() and apply_cli_session_continuity() - Wire project store in handle_direct_prompt and praison run actions mode - Preserve session args across praison.run() arg re-parse for YAML runs - Respect caller-injected session store in _init_session_store() - Fix --output actions crash from invalid resume_session Agent kwarg Co-authored-by: Mervin Praison <MervinPraison@users.noreply.github.com>
1 parent 1ad58ca commit e671898

5 files changed

Lines changed: 137 additions & 48 deletions

File tree

src/praisonai-agents/praisonaiagents/agent/memory_mixin.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,8 +317,9 @@ def _init_session_store(self):
317317
return
318318

319319
try:
320-
from ..session import get_default_session_store
321-
self._session_store = get_default_session_store()
320+
if self._session_store is None:
321+
from ..session import get_default_session_store
322+
self._session_store = get_default_session_store()
322323

323324
# Restore chat history from previous session
324325
history = self._session_store.get_chat_history(self._session_id)

src/praisonai/praisonai/cli/commands/run.py

Lines changed: 8 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ class Args:
256256
args = Args()
257257
args.auto_save = auto_save_name
258258
args.resume_session = session_id
259+
args.cli_project_sessions = bool(auto_save_name)
259260

260261
praison.args = args
261262

@@ -349,10 +350,9 @@ def _run_prompt(
349350
if not no_save:
350351
import uuid
351352
auto_save_name = session_id or "session-" + str(uuid.uuid4())[:8]
352-
353-
# If output_mode is "actions", use direct Agent with actions preset
354353
if output_mode == "actions":
355354
from praisonaiagents import Agent
355+
from ..state.project_sessions import build_cli_memory_config, apply_cli_session_continuity
356356

357357
agent_config = {
358358
"name": "RunAgent",
@@ -370,13 +370,13 @@ def _run_prompt(
370370
approval, all_tools=approve_all_tools, timeout=approval_timeout,
371371
)
372372

373-
# Add session support to Agent if needed
374-
if session_id:
375-
agent_config["resume_session"] = session_id
376-
if auto_save_name:
377-
agent_config["auto_save"] = auto_save_name
373+
memory_cfg = build_cli_memory_config(session_id, auto_save_name)
374+
if memory_cfg is not None:
375+
agent_config["memory"] = memory_cfg
378376

379377
agent = Agent(**agent_config)
378+
if session_id or auto_save_name:
379+
apply_cli_session_continuity(agent, session_id or auto_save_name)
380380
result = agent.start(prompt)
381381

382382
output.emit_result(
@@ -413,6 +413,7 @@ class Args:
413413
args.auto_save = auto_save_name
414414
args.history = None
415415
args.resume_session = session_id
416+
args.cli_project_sessions = bool(auto_save_name)
416417
args.include_rules = None if no_rules else "auto"
417418
args.no_rules = no_rules
418419
args.workflow = None
@@ -440,44 +441,6 @@ class Args:
440441

441442
praison.args = args
442443

443-
# If output_mode is "actions", use direct Agent with actions preset
444-
if output_mode == "actions":
445-
from praisonaiagents import Agent
446-
447-
agent_config = {
448-
"name": "RunAgent",
449-
"role": "Assistant",
450-
"goal": "Complete the task",
451-
"output": "actions", # Use actions preset
452-
}
453-
if model:
454-
agent_config["llm"] = model
455-
456-
# Resolve approval backend if specified
457-
if approval:
458-
from praisonai.cli.features.approval import resolve_approval_config
459-
agent_config["approval"] = resolve_approval_config(
460-
approval, all_tools=approve_all_tools, timeout=approval_timeout,
461-
)
462-
463-
# Add session support to Agent if needed
464-
if session_id:
465-
agent_config["resume_session"] = session_id
466-
if auto_save_name:
467-
agent_config["auto_save"] = auto_save_name
468-
469-
agent = Agent(**agent_config)
470-
result = agent.start(prompt)
471-
472-
output.emit_result(
473-
message="Prompt completed",
474-
data={"result": str(result) if result else None}
475-
)
476-
477-
# Don't print result again - actions mode already shows output
478-
return
479-
480-
# Use handle_direct_prompt for other modes
481444
result = praison.handle_direct_prompt(prompt)
482445

483446
output.emit_result(

src/praisonai/praisonai/cli/main.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,13 +362,20 @@ def main(self):
362362
original_agent_file = self.agent_file
363363

364364
# Parse args - this returns both args and unknown_args
365+
preserved_args = getattr(self, 'args', None)
365366
parse_result = self.parse_args()
366367
if isinstance(parse_result, tuple):
367368
args, unknown_args = parse_result
368369
else:
369370
args = parse_result
370371
unknown_args = []
371372

373+
# Preserve project session flags set by ``praison run`` before parse_args()
374+
if preserved_args and getattr(preserved_args, 'cli_project_sessions', False):
375+
for attr in ('auto_save', 'resume_session', 'cli_project_sessions'):
376+
if hasattr(preserved_args, attr):
377+
setattr(args, attr, getattr(preserved_args, attr))
378+
372379
# Store args for use in handle_direct_prompt
373380
self.args = args
374381
invocation_cmd = "praisonai"
@@ -4243,6 +4250,15 @@ def _extract_cli_config_for_yaml(self):
42434250
handoff_detect_cycles = getattr(self.args, 'handoff_detect_cycles', None)
42444251
if handoff_detect_cycles is not None:
42454252
cli_config['handoff_detect_cycles'] = handoff_detect_cycles
4253+
4254+
if getattr(self.args, 'cli_project_sessions', False):
4255+
from .state.project_sessions import build_cli_memory_config
4256+
memory_cfg = build_cli_memory_config(
4257+
getattr(self.args, 'resume_session', None),
4258+
getattr(self.args, 'auto_save', None),
4259+
)
4260+
if memory_cfg is not None:
4261+
cli_config['memory'] = memory_cfg
42464262

42474263
return cli_config
42484264

@@ -4509,6 +4525,15 @@ def handle_direct_prompt(self, prompt):
45094525
else:
45104526
agent_config["memory"] = True
45114527
print("[bold cyan]Memory enabled - agent will remember context across sessions[/bold cyan]")
4528+
elif getattr(self.args, 'cli_project_sessions', False) and (
4529+
getattr(self.args, 'resume_session', None) or getattr(self.args, 'auto_save', None)
4530+
):
4531+
from .state.project_sessions import build_cli_memory_config
4532+
agent_config["memory"] = build_cli_memory_config(
4533+
getattr(self.args, 'resume_session', None),
4534+
getattr(self.args, 'auto_save', None),
4535+
)
4536+
print(f"[bold cyan]Project session enabled - session '{agent_config['memory'].auto_save}'[/bold cyan]")
45124537
elif getattr(self.args, 'auto_save', None):
45134538
from praisonaiagents import MemoryConfig
45144539
agent_config["memory"] = MemoryConfig(auto_save=self.args.auto_save)
@@ -4792,6 +4817,12 @@ def level_based_approve(function_name, arguments, risk_level):
47924817
flow.display_workflow_start("Direct Prompt", ["DirectAgent"])
47934818

47944819
agent = PraisonAgent(**agent_config)
4820+
4821+
if getattr(self.args, 'cli_project_sessions', False):
4822+
session_id = getattr(self.args, 'resume_session', None) or getattr(self.args, 'auto_save', None)
4823+
if session_id:
4824+
from .state.project_sessions import apply_cli_session_continuity
4825+
apply_cli_session_continuity(agent, session_id)
47954826

47964827
# AutoRag - Automatic RAG retrieval decision
47974828
if hasattr(self, 'args') and getattr(self.args, 'auto_rag', False):

src/praisonai/praisonai/cli/state/project_sessions.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,41 @@ def find_last_session(project_path: Optional[str] = None) -> Optional[str]:
9898
Session ID or None if no sessions exist
9999
"""
100100
store = get_project_session_store(project_path)
101-
return store.get_last_session_id()
101+
return store.get_last_session_id()
102+
103+
104+
def build_cli_memory_config(
105+
session_id: Optional[str] = None,
106+
auto_save: Optional[str] = None,
107+
):
108+
"""Build MemoryConfig for ``praison run`` project-scoped session continuity."""
109+
if not session_id and not auto_save:
110+
return None
111+
112+
from praisonaiagents import MemoryConfig
113+
114+
sid = session_id or auto_save
115+
save_name = auto_save or sid
116+
return MemoryConfig(session_id=sid, auto_save=save_name, history=True)
117+
118+
119+
def apply_cli_session_continuity(agent, session_id: str, project_path: Optional[str] = None) -> None:
120+
"""Wire an agent to the project session store and restore prior history."""
121+
store = get_project_session_store(project_path)
122+
agent._session_store = store
123+
agent._session_id = session_id
124+
agent._history_enabled = True
125+
agent._history_session_id = session_id
126+
if not getattr(agent, "auto_save", None):
127+
agent.auto_save = session_id
128+
129+
history = store.get_chat_history(session_id)
130+
if history and not agent.chat_history:
131+
for msg in history:
132+
agent.chat_history.append({
133+
"role": msg["role"],
134+
"content": msg["content"],
135+
})
136+
agent._auto_save_last_index = len(agent.chat_history)
137+
138+
agent._session_store_initialized = True
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"""Tests for praison run project-scoped session continuity."""
2+
3+
from praisonai.cli.state.project_sessions import (
4+
apply_cli_session_continuity,
5+
build_cli_memory_config,
6+
get_project_session_store,
7+
)
8+
from praisonaiagents import Agent
9+
10+
11+
def test_build_cli_memory_config_enables_history():
12+
cfg = build_cli_memory_config(session_id="sess-1", auto_save="sess-1")
13+
assert cfg is not None
14+
assert cfg.session_id == "sess-1"
15+
assert cfg.auto_save == "sess-1"
16+
assert cfg.history is True
17+
18+
19+
def test_apply_cli_session_continuity_restores_project_history():
20+
store = get_project_session_store()
21+
session_id = "continuity-unit-test"
22+
store.clear_session(session_id)
23+
store.add_user_message(session_id, "remember this")
24+
store.add_assistant_message(session_id, "acknowledged")
25+
26+
agent = Agent(name="RunAgent", memory=build_cli_memory_config(session_id, session_id))
27+
apply_cli_session_continuity(agent, session_id)
28+
29+
assert agent._session_store.session_dir == store.session_dir
30+
assert agent._session_id == session_id
31+
assert len(agent.chat_history) == 2
32+
assert agent.chat_history[0]["content"] == "remember this"
33+
34+
agent.chat_history.append({"role": "user", "content": "follow-up"})
35+
agent.chat_history.append({"role": "assistant", "content": "reply"})
36+
agent._auto_save_session()
37+
38+
saved = store.get_chat_history(session_id)
39+
assert len(saved) == 4
40+
assert saved[-1]["content"] == "reply"
41+
42+
store.clear_session(session_id)
43+
44+
45+
def test_injected_session_store_not_overwritten():
46+
store = get_project_session_store()
47+
session_id = "store-injection-test"
48+
store.clear_session(session_id)
49+
50+
agent = Agent(name="RunAgent", memory=build_cli_memory_config(session_id, session_id))
51+
apply_cli_session_continuity(agent, session_id)
52+
injected_dir = agent._session_store.session_dir
53+
54+
agent._init_session_store()
55+
assert agent._session_store.session_dir == injected_dir
56+
57+
store.clear_session(session_id)

0 commit comments

Comments
 (0)