From e03a1f4e37c0d0ad680820e1354e42b60bca5cae Mon Sep 17 00:00:00 2001 From: "Ryan H. Tran" Date: Wed, 20 Aug 2025 21:26:55 +0700 Subject: [PATCH] Move TASKS.md to session-specific directory in `~/.openhands` (#10493) Co-authored-by: openhands --- openhands/runtime/base.py | 49 +++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/openhands/runtime/base.py b/openhands/runtime/base.py index d758c9802b..624d7b6171 100644 --- a/openhands/runtime/base.py +++ b/openhands/runtime/base.py @@ -67,6 +67,7 @@ from openhands.runtime.plugins import ( from openhands.runtime.runtime_status import RuntimeStatus from openhands.runtime.utils.edit import FileEditRuntimeMixin from openhands.runtime.utils.git_handler import CommandResult, GitHandler +from openhands.storage.locations import get_conversation_dir from openhands.utils.async_utils import ( GENERAL_TIMEOUT, call_async_from_sync, @@ -876,8 +877,14 @@ fi if isinstance(action, AgentThinkAction): return AgentThinkObservation('Your thought has been logged.') elif isinstance(action, TaskTrackingAction): - # If `command` is `plan`, write the serialized task list to the file TASKS.md under `.openhands/` + # Get the session-specific task file path + conversation_dir = get_conversation_dir( + self.sid, self.event_stream.user_id + ) + task_file_path = f'{conversation_dir}TASKS.md' + if action.command == 'plan': + # Write the serialized task list to the session directory content = '# Task List\n\n' for i, task in enumerate(action.task_list, 1): status_icon = { @@ -886,33 +893,39 @@ fi 'done': '✅', }.get(task.get('status', 'todo'), '⏳') content += f'{i}. {status_icon} {task.get("title", "")}\n{task.get("notes", "")}\n' - write_obs = self.write( - FileWriteAction(path='.openhands/TASKS.md', content=content) - ) - if isinstance(write_obs, ErrorObservation): + + try: + self.event_stream.file_store.write(task_file_path, content) + return TaskTrackingObservation( + content=f'Task list has been updated with {len(action.task_list)} items. Stored in session directory: {task_file_path}', + command=action.command, + task_list=action.task_list, + ) + except Exception as e: return ErrorObservation( - f'Failed to write task list to .openhands/TASKS.md: {write_obs.content}' + f'Failed to write task list to session directory {task_file_path}: {str(e)}' ) - return TaskTrackingObservation( - content=f'Task list has been updated with {len(action.task_list)} items.', - command=action.command, - task_list=action.task_list, - ) elif action.command == 'view': - # If `command` is `view`, read the TASKS.md file and return its content - read_obs = self.read(FileReadAction(path='.openhands/TASKS.md')) - if isinstance(read_obs, FileReadObservation): + # Read the TASKS.md file from the session directory + try: + content = self.event_stream.file_store.read(task_file_path) return TaskTrackingObservation( - content=read_obs.content, + content=content, command=action.command, task_list=[], # Empty for view command ) - else: - return TaskTrackingObservation( # Return observation if error occurs because file might not exist yet + except FileNotFoundError: + return TaskTrackingObservation( command=action.command, task_list=[], - content=f'Failed to read the task list. Error: {read_obs.content}', + content='No task list found. Use the "plan" command to create one.', + ) + except Exception as e: + return TaskTrackingObservation( + command=action.command, + task_list=[], + content=f'Failed to read the task list from session directory {task_file_path}. Error: {str(e)}', ) return NullObservation('')