You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Bring PraisonAI's bot / gateway default agent up to parity with two reference implementations — hermes-agent (~/hermes-agent) and OpenClaw (~/openclaw) — by shipping (1) a richer default tool set, (2) a full self-improving "skills" write path (skill_manage), and (3) a per-bot / per-session workspace sandbox that scopes every file-touching tool to a single directory with read/write/read-only/none access modes.
After #1498 the bot gets a small set of safe defaults (search_web, web_crawl, schedule_*, store/search_memory, store/search_learning). Hermes-agent and OpenClaw ship file I/O, shell / code execution, planning (todo), delegation / subagents, session history search, and skill management out of the box — all scoped to an explicit workspace directory. PraisonAI currently has none of those gated tools wired into bot defaults, no skill_manage tool, and no workspaceDir concept (file tools fall back to os.getcwd() of the daemon, which is not per-session).
This issue delivers all three gaps in one coordinated change, preserving backward compatibility and the "safe by default" invariant.
Background
Hermes-agent (~/hermes-agent)
Default core toolset (~/hermes-agent/toolsets.py, _HERMES_CORE_TOOLS, lines 31-63) — shared across CLI and every messaging-platform bot:
No file I/O, no shell, no planning, no delegation, no session_search, no skill management — explicitly excluded as "destructive" in _get_default_safe_tools.
Skills system (src/praisonai-agents/praisonaiagents/skills/) is read/activate only:
There is no create_skill / edit_skill / patch_skill / delete_skill / write_skill_file — the agent cannot turn successful runs into reusable procedural memory.
Workspace — there is no bot/gateway workspace concept:
FileTools._validate_path (src/praisonai-agents/praisonaiagents/tools/file_tools.py:25-56) uses os.getcwd() as the allowed root — which in bot mode is wherever the daemon was started (often / or the user's home).
BotConfig has no workspace_dir / workspace_access / workspace_scope fields.
SkillTools.__init__(working_directory=None) defaults to os.getcwd() — same issue.
No per-user, per-channel, or per-session workspace isolation.
All file tools are constructed with the bot's Workspace, so enabling them by default is safe — the agent physically cannot read or write outside workspace_dir.
execute_command / shell_command remain opt-in (listed in destructive_tools set).
Build Workspace from config; construct file / skill tools with workspace; expand default list; move new file tools out of destructive_tools
src/praisonai/praisonai/bots/bot.py
Resolve default workspace_dir → ~/.praisonai/workspaces/<bot_name>/<session_key>; create dir on bot start
src/praisonai/praisonai/gateway/server.py
Same workspace wiring for WebSocketGateway (keeps #1498 parity)
docs/features/bots.mdx (PraisonAIDocs)
Document workspace_* fields, new defaults, skill_manage
docs/concepts/skills.mdx
Document self-improving workflow
Technical Considerations
Dependencies: no new runtime deps. Fuzzy patch reuses existing difflib. Skill scan can reuse ast + regex patterns already present for tools/skill_bridge.py.
Performance: Workspace.resolve() calls Path.resolve() once per tool call — negligible. No module-level imports added to praisonaiagents.__init__; skills write path is lazy-loaded (only loaded when skill_manage is invoked). Verified import time budget < 200 ms.
Backward compat: All new fields default to current behaviour. FileTools() with no workspace works exactly as today (os.getcwd()). SkillManager without write calls is unchanged. Existing BotConfig(default_tools=[…]) still honoured.
Safe by default: Workspace root defaults to ~/.praisonai/workspaces/<bot>/<session> — not the daemon's cwd. File tools, while now in default_tools, can only touch the workspace. skill_manage is auto-approvable because the write surface is ~/.praisonai/skills/ + the workspace, nothing else. execute_command remains opt-in.
Multi-agent safety: Workspace is frozen, passed by value. Each bot session gets its own instance keyed by session_key. No shared mutable state.
Async-safe: All new tool methods follow existing sync/async patterns. File I/O remains sync (wrapped by existing asyncio.to_thread shim used elsewhere in the SDK).
Acceptance Criteria
Workspace
praisonaiagents.workspace.Workspace(root, access, scope) exists and is exported
frompraisonaiagentsimportAgentfrompraisonaiagents.botsimportBot, BotConfigbot=Bot(name="scribe", config=BotConfig(workspace_dir="./scratch"))
# No tools passed → smart defaults applyr1=bot.chat("Save a skill called 'weekly-report' that produces a markdown summary from a CSV path")
r2=bot.chat("Use the weekly-report skill on ./data/jan.csv")
assert"weekly-report"inr2# agent created and then invoked its own skill
Implementation Notes
Key files to read first
~/hermes-agent/tools/skill_manager_tool.py (789 lines) — reference implementation of skill_manage + security model
~/openclaw/src/agents/sandbox/context.ts (256 lines) — reference implementation of workspace scope/access modes
_get_default_safe_tools(config) must call a new _resolve_with_workspace(tool_names, workspace) that instantiates FileTools(workspace=...), SkillTools(workspace=...) etc. rather than returning globally-shared tool functions.
Bot.start() must materialise the workspace dir before the first agent turn.
apply_bot_smart_defaults(agent, config) must set agent._workspace = workspace so any future tool the user adds can introspect it.
SkillManager.create_skill must call clear_skills_system_prompt_cache() equivalent so the next turn's prompt includes the new skill (hermes pattern, tools/skill_manager_tool.py:668-672).
Overview
Bring PraisonAI's bot / gateway default agent up to parity with two reference implementations —
hermes-agent(~/hermes-agent) andOpenClaw(~/openclaw) — by shipping (1) a richer default tool set, (2) a full self-improving "skills" write path (skill_manage), and (3) a per-bot / per-session workspace sandbox that scopes every file-touching tool to a single directory with read/write/read-only/none access modes.After #1498 the bot gets a small set of safe defaults (
search_web,web_crawl,schedule_*,store/search_memory,store/search_learning). Hermes-agent and OpenClaw ship file I/O, shell / code execution, planning (todo), delegation / subagents, session history search, and skill management out of the box — all scoped to an explicit workspace directory. PraisonAI currently has none of those gated tools wired into bot defaults, noskill_managetool, and noworkspaceDirconcept (file tools fall back toos.getcwd()of the daemon, which is not per-session).This issue delivers all three gaps in one coordinated change, preserving backward compatibility and the "safe by default" invariant.
Background
Hermes-agent (
~/hermes-agent)Default core toolset (
~/hermes-agent/toolsets.py,_HERMES_CORE_TOOLS, lines 31-63) — shared across CLI and every messaging-platform bot:web_search,web_extractterminal,processread_file,write_file,patch,search_filesvision_analyze,image_generateskills_list,skill_view,skill_managebrowser_navigate,browser_snapshot,browser_click,browser_type, …todo,memory,session_search,clarifyexecute_code,delegate_taskcronjob,send_messageha_*(gated on env)Skills lifecycle (
~/hermes-agent/tools/skill_manager_tool.py) — the self-improving agent concept:~/.hermes/skills/<name>/SKILL.md+references/,templates/,scripts/,assets/skills_guard.scan_skill(prompt-injection scan, rollback on block)os.replacepath_security.validate_within_dircontainment check +has_traversal_componentguardMAX_NAME_LENGTH=64,MAX_SKILL_CONTENT_CHARS=100_000,MAX_SKILL_FILE_BYTES=1_048_576patchaction shared with file-patch tool (fuzzy_find_and_replace)Workspace scoping: weaker — relies on
terminal.cwd+ optional Docker sandbox. Skills dir is the only hard-enforced boundary.OpenClaw (
~/openclaw)Default bot toolset (
~/openclaw/src/agents/openclaw-tools.ts:230-303) — every tool factory is passed an explicitworkspaceDir:canvas,nodes,cron,message,tts,image_generate,music_generate,video_generate,gateway,agents_list,update_plan,sessions_list,sessions_history,sessions_send,sessions_yield,sessions_spawn,subagents,session_status,web_search,web_fetch,image(vision),pdf, + plugin toolsSandbox tool policy (
~/openclaw/src/agents/sandbox/constants.ts:13-38):Workspace system — the piece PraisonAI is missing:
DEFAULT_AGENT_WORKSPACE_DIRconstant +normalizeWorkspaceDir()/resolveWorkspaceRoot()(~/openclaw/src/agents/workspace-dir.ts) — refuses filesystem root, expands~, defaults toprocess.cwd().DEFAULT_SANDBOX_WORKSPACE_ROOT = path.join(STATE_DIR, "sandboxes")— per-session sandbox copies live here (~/openclaw/src/agents/sandbox/constants.ts:5).ensureSandboxWorkspaceLayout()(~/openclaw/src/agents/sandbox/context.ts:26-82) decides the effective workspace based on:cfg.scope ∈ {"shared", "session", "agent"}→resolveSandboxScopeKey(...)cfg.workspaceAccess ∈ {"rw", "ro", "none"}→rwuses the real agent dir,ro/noneuse a copied sandbox dirworkspaceDir+ optionalsandbox: { root, bridge }+fsPolicy: ToolFsPolicy. Tools that read or write outside the workspace are rejected upstream.applyNodesToolWorkspaceGuard()wraps any tool that can touch the filesystem to enforce containment.syncSkillsToWorkspacewhen scope is non-rw.PraisonAI — current state
Bot defaults (
src/praisonai-agents/praisonaiagents/bots/config.py:53-58+src/praisonai/praisonai/bots/_defaults.py):No file I/O, no shell, no planning, no delegation, no
session_search, no skill management — explicitly excluded as "destructive" in_get_default_safe_tools.Skills system (
src/praisonai-agents/praisonaiagents/skills/) is read/activate only:SkillManager(manager.py) exposesdiscover,add_skill,get_skill,activate,invoke,to_prompt,get_instructions,load_resources,clear.tools/skill_tools.pyexposesrun_skill_script(execute only,@require_approval("critical")).create_skill/edit_skill/patch_skill/delete_skill/write_skill_file— the agent cannot turn successful runs into reusable procedural memory.Workspace — there is no bot/gateway workspace concept:
FileTools._validate_path(src/praisonai-agents/praisonaiagents/tools/file_tools.py:25-56) usesos.getcwd()as the allowed root — which in bot mode is wherever the daemon was started (often/or the user's home).BotConfighas noworkspace_dir/workspace_access/workspace_scopefields.SkillTools.__init__(working_directory=None)defaults toos.getcwd()— same issue.Architecture Analysis
Key file locations
src/praisonai-agents/praisonaiagents/bots/config.pyBotConfigdataclass (default_tools, auto_approve_tools)workspace_*fieldssrc/praisonai/praisonai/bots/_defaults.pyapply_bot_smart_defaults+_get_default_safe_toolssrc/praisonai-agents/praisonaiagents/tools/file_tools.pyFileTools.read_file/write_file/list_files/copy_file/delete_file_validate_pathhard-codesos.getcwd()— needs injectable rootsrc/praisonai-agents/praisonaiagents/tools/skill_tools.pySkillTools.run_skill_scriptskill_manageequivalentsrc/praisonai-agents/praisonaiagents/skills/manager.pySkillManager(read/activate)create,edit,patch,delete,write_file,remove_file)src/praisonai-agents/praisonaiagents/skills/discovery.pysrc/praisonai-agents/praisonaiagents/tools/path_overlap.pyvalidate_within_dirportsrc/praisonai/praisonai/bots/bot.pyBotclass — wiresBotConfig→Agentsrc/praisonai/praisonai/gateway/server.pyBot(per #1498 fix)Control flow
Gap Analysis Summary
Critical gaps
skill_managewrite APIread_file,write_file,edit_file,todo,subagentsFileTools._validate_pathcoupled toos.getcwd()Feature gaps (hermes/openclaw → praisonai)
read_fileread)write_filewrite)edit_file/patchedit,apply_patch)search_filesexecute_code/ terminalexec,process)shell_toolsexists, gatedtodoplanningupdate_plan)delegate_task/subagentssubagent_toolexists, not defaultedsession_search/sessions_historyskills_list/skill_viewSkillManageronly)skill_manage(write)web_search/web_extractvision_analyze/image_generateProposed Implementation
Phase 1 — Workspace scope (foundation)
Add an explicit
Workspacedataclass in the core SDK and thread it through bot config + every file-touching tool.BotConfigadditions (src/praisonai-agents/praisonaiagents/bots/config.py):File tools refactor —
FileToolsbecomes workspace-aware (backward compatible):Phase 2 — Skill write API (self-improving agent)
Extend
SkillManagerwith the hermesskill_managesurface:Expose as an agent tool (
src/praisonai-agents/praisonaiagents/tools/skill_tools.py):Security guardrails (port from hermes
skills_guard):^[a-z0-9][a-z0-9._-]*$, max 64 chars..traversal rejection +validate_within_dir(path, SKILLS_DIR)containmenttempfile.mkstemp+os.replaceskills_guardported)Phase 3 — Expanded bot defaults
Update
_get_default_safe_tools(src/praisonai/praisonai/bots/_defaults.py) +BotConfig.default_tools:All file tools are constructed with the bot's
Workspace, so enabling them by default is safe — the agent physically cannot read or write outsideworkspace_dir.execute_command/shell_commandremain opt-in (listed indestructive_toolsset).Files to Create / Modify
New files
src/praisonai-agents/praisonaiagents/workspace/__init__.pyWorkspacedataclass + helperssrc/praisonai-agents/praisonaiagents/workspace/protocols.pyWorkspaceProtocol,FsPolicyProtocolsrc/praisonai-agents/praisonaiagents/skills/guard.pyscan_skill,should_allow_install(port from hermes)src/praisonai-agents/praisonaiagents/tools/edit_tool.pysrc/praisonai-agents/tests/unit/workspace/test_workspace.py..rejection,rw/ro/nonemodessrc/praisonai-agents/tests/unit/skills/test_skill_manage.pysrc/praisonai/tests/unit/bots/test_workspace_sandbox.pysrc/praisonai/tests/integration/bots/test_skill_manage_daemon.pyexamples/python/bots/self_improving_bot.pyModified files
src/praisonai-agents/praisonaiagents/bots/config.pyworkspace_dir,workspace_access,workspace_scope; expanddefault_tools; add fields toto_dictsrc/praisonai-agents/praisonaiagents/tools/file_tools.pyworkspace;_validate_pathuses workspace when setsrc/praisonai-agents/praisonaiagents/tools/skill_tools.pyskill_manage,skill_view,skills_listmethods; workspace-awaresrc/praisonai-agents/praisonaiagents/skills/manager.pycreate_skill,edit_skill,patch_skill,delete_skill,write_skill_file,remove_skill_filesrc/praisonai-agents/praisonaiagents/skills/__init__.pySkillManagerWriteMixinsrc/praisonai-agents/praisonaiagents/tools/__init__.pyedit_file,search_files,todo_*,skill_manage,skill_view,skills_list,session_searchsrc/praisonai/praisonai/bots/_defaults.pyWorkspacefrom config; construct file / skill tools with workspace; expand default list; move new file tools out ofdestructive_toolssrc/praisonai/praisonai/bots/bot.pyworkspace_dir→~/.praisonai/workspaces/<bot_name>/<session_key>; create dir on bot startsrc/praisonai/praisonai/gateway/server.pyWebSocketGateway(keeps #1498 parity)docs/features/bots.mdx(PraisonAIDocs)workspace_*fields, new defaults,skill_managedocs/concepts/skills.mdxTechnical Considerations
difflib. Skill scan can reuseast+ regex patterns already present fortools/skill_bridge.py.Workspace.resolve()callsPath.resolve()once per tool call — negligible. No module-level imports added topraisonaiagents.__init__; skills write path is lazy-loaded (only loaded whenskill_manageis invoked). Verified import time budget < 200 ms.FileTools()with no workspace works exactly as today (os.getcwd()).SkillManagerwithout write calls is unchanged. ExistingBotConfig(default_tools=[…])still honoured.~/.praisonai/workspaces/<bot>/<session>— not the daemon's cwd. File tools, while now indefault_tools, can only touch the workspace.skill_manageis auto-approvable because the write surface is~/.praisonai/skills/+ the workspace, nothing else.execute_commandremains opt-in.Workspaceis frozen, passed by value. Each bot session gets its own instance keyed bysession_key. No shared mutable state.asyncio.to_threadshim used elsewhere in the SDK).Acceptance Criteria
Workspace
praisonaiagents.workspace.Workspace(root, access, scope)exists and is exportedWorkspace.resolve("foo.txt")returns absolute path insideroot;Workspace.resolve("../etc/passwd")raisesValueErrorBotConfig.workspace_dir/workspace_access/workspace_scoperound-trip throughto_dictBot()with noworkspace_dirauto-creates~/.praisonai/workspaces/<bot_name>/<session_key>/on startSkills (self-improving)
SkillManager.create_skill(name, content)writes~/.praisonai/skills/<name>/SKILL.mdatomically with valid frontmatterSkillManager.patch_skill(name, old_string, new_string)performs fuzzy replace; rejects non-unique match withoutreplace_allSkillManager.write_skill_file(name, "references/api.md", content)accepts allowed subdirs; rejects arbitrary pathsskill_managewrite, nextSkillManager.to_prompt()includes the new skill (cache busted)skill_manageis function-call compatible with hermes schema (actions: create/edit/patch/delete/write_file/remove_file)Bot defaults
Bot()with no tools gets: web, memory, learning, schedule, file (RW in workspace), todo, skills, delegation, session_searchexecute_command,shell_command,delete_fileremain off unless explicitly listedBotConfig(default_tools=[])still yields zero auto-injected toolsReal agentic test (required)
Implementation Notes
Key files to read first
~/hermes-agent/tools/skill_manager_tool.py(789 lines) — reference implementation ofskill_manage+ security model~/openclaw/src/agents/sandbox/context.ts(256 lines) — reference implementation of workspace scope/access modes~/openclaw/src/agents/workspace-dir.ts(21 lines) — minimal workspace root normalizationsrc/praisonai-agents/praisonaiagents/skills/manager.py(262 lines) — existing read-only SkillManager to extendsrc/praisonai-agents/praisonaiagents/tools/file_tools.py(441 lines) — existing FileTools to make workspace-awaresrc/praisonai/praisonai/bots/_defaults.py(post-fix: Bot has zero tools in daemon/gateway mode — smart defaults + auto-approve now run #1498) — existing smart-defaults injection pointCritical integration points
_get_default_safe_tools(config)must call a new_resolve_with_workspace(tool_names, workspace)that instantiatesFileTools(workspace=...),SkillTools(workspace=...)etc. rather than returning globally-shared tool functions.Bot.start()must materialise the workspace dir before the first agent turn.apply_bot_smart_defaults(agent, config)must setagent._workspace = workspaceso any future tool the user adds can introspect it.SkillManager.create_skillmust callclear_skills_system_prompt_cache()equivalent so the next turn's prompt includes the new skill (hermes pattern,tools/skill_manager_tool.py:668-672).Botmust share the sameapply_bot_smart_defaultscode path — do not fork.Testing commands
References
~/hermes-agent/toolsets.py~/hermes-agent/tools/skill_manager_tool.py~/hermes-agent/tools/path_security.py~/openclaw/src/agents/workspace-dir.ts~/openclaw/src/agents/sandbox/context.ts~/openclaw/src/agents/sandbox/constants.ts~/openclaw/src/agents/sandbox/tool-policy.ts