Skip to main content
Any plan, todo list, or PRP name you pass in — even one an LLM made up — is written inside its storage directory. ../../x can never escape, and ordinary names keep the exact filename they always had.

Quick Start

1

Let an agent name what it saves

2

Any name is confined


How It Works

Only characters that could change the path are percent-encoded: %, /, \, :, *, ?, ", <, >, |, control characters, and a leading .. Everything else — including spaces and non-ASCII — is kept as-is. list_todos() decodes filenames back to the original names, so whatever it returns works with load_todo() and delete_todo().

Which APIs use it

You can use the same helper for your own storage:
Legacy compatibility: todo files written before PraisonAI PR #6239 with plain names are still found by load_todo and delete_todo — no migration needed.

Common Patterns


Best Practices

Don’t pre-sanitise names yourself — PlanStorage already confines them, and extra mangling breaks the round-trip from list_todos().
Always use names returned by list_todos() rather than reading filenames from disk; the listed form is the one load_todo() and delete_todo() expect.
When your own tool writes a file named by an LLM or user, build the path with safe_join(base, name, suffix) instead of os.path.join.

Planning

Plan-then-execute agents

Todo Planning

Agents that track todo lists

Session Store

Collision-safe session filenames

Async Bridge

Shared sync↔async bridge