feat!: Multi-agent/multi-workspace architecture#1375
feat!: Multi-agent/multi-workspace architecture#1375xieyxclack merged 28 commits intoagentscope-ai:mainfrom
Conversation
Made-with: Cursor
Resolved conflicts in:
- console/src/layouts/MainLayout/index.tsx: merged Chat optimization (display:none) with /agents route
- console/src/locales/{en,zh}.json: merged "help", "actions", "total" keys
- console/src/pages/Agent/Config/index.tsx: adopted upstream refactoring with useAgentConfig hook
- console/src/pages/Settings/Security/index.tsx: adopted upstream refactoring with useToolGuard hook
- src/copaw/app/routers/skills.py: preserved local workspace-aware SkillService implementation
Upstream changes merged:
- feat(skills): AI skill optimization with streaming support
- feat(skills): show metadata description in unified card layout
- feat(console): preserve chat status when navigating away
- refactor(console): Config & Security page refactoring
- refactor(session): sync session state saving
- refactor(model): integrate TokenRecordingModelWrapper
- fix(provider): avoid triggering fetch models every time
- fix(config): align channel config types
- perf(console): optimize multiple chat requests
All tests passing (58/58).
Made-with: Cursor
There was a problem hiding this comment.
Pull request overview
This PR is a WIP preview of a breaking multi-agent, multi-workspace architecture for CoPaw, moving agent-specific state/config into per-agent workspace directories while keeping a smaller global config.json and updating backend APIs + console UI to support agent switching.
Changes:
- Introduces
Workspace+MultiAgentManagerto run multiple isolated agent runtimes concurrently, with per-agentagent.json, chats, jobs, memory, and skills directories. - Refactors backend routers and agent runtime (runner/agent/tools/skills/mcp/channels) to resolve configuration and filesystem operations in an agent-scoped way.
- Adds console UI support for listing/creating/updating/activating agents and propagates
X-Agent-Idacross requests.
Reviewed changes
Copilot reviewed 63 out of 63 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| src/copaw/config/context.py | Adds a ContextVar to carry the active workspace directory into tool execution. |
| src/copaw/config/config.py | Adds agent profile models, root-agent reference structure, agent.json load/save helpers, and legacy migration logic. |
| src/copaw/cli/skills_cmd.py | Updates CLI skills operations to use a workspace dir (still global WORKING_DIR for now). |
| src/copaw/cli/init_cmd.py | Passes explicit workspace_dir into skill sync and handles optional agent language. |
| src/copaw/app/workspace_restart.py | Adds workspace-scoped restart helper for hot reload. |
| src/copaw/app/workspace.py | Implements the per-agent Workspace runtime (runner/chat/memory/mcp/channels/cron). |
| src/copaw/app/runner/runner.py | Makes AgentRunner agent-aware (agent_id/workspace_dir) and loads agent config per query. |
| src/copaw/app/runner/repo/base.py | Adds debug logging around chat lookup. |
| src/copaw/app/runner/manager.py | Adds verbose logging for chat auto-registration flow. |
| src/copaw/app/runner/command_dispatch.py | Moves ReMe import to lazy import to avoid module import-time dependency issues. |
| src/copaw/app/runner/api.py | Resolves chat manager/session via active agent workspace instead of global app state. |
| src/copaw/app/routers/workspace.py | Scopes workspace download/upload to the active agent workspace directory. |
| src/copaw/app/routers/tools.py | Makes tools listing/toggling agent-scoped and triggers background reload. |
| src/copaw/app/routers/skills.py | Makes skills endpoints agent-scoped and routes skill hub install into a workspace. |
| src/copaw/app/routers/mcp.py | Makes MCP CRUD operations agent-scoped and triggers background reload. |
| src/copaw/app/routers/config.py | Makes channel + heartbeat configuration agent-scoped (partially keeps some global settings). |
| src/copaw/app/routers/agents.py | Adds REST API for multi-agent CRUD + activation + workspace file listing/reading/writing. |
| src/copaw/app/routers/agent_scoped.py | Adds a wrapper router intended to mount existing routers under /agents/{agentId}/.... |
| src/copaw/app/routers/agent.py | Refactors agent file/memory + running-config + system-prompt endpoints to use active agent workspace. |
| src/copaw/app/routers/init.py | Registers the new agents router and exports agent-scoped router factory. |
| src/copaw/app/multi_agent_manager.py | Adds manager to lazily start/stop/reload workspaces by agent_id. |
| src/copaw/app/migration.py | Adds migration to create default agent workspace and update root config structure. |
| src/copaw/app/crons/repo/json_repo.py | Allows cron repo path to be provided as `Path |
| src/copaw/app/crons/api.py | Resolves cron manager via active agent workspace. |
| src/copaw/app/channels/manager.py | Skips channel startup when channel config indicates it’s disabled. |
| src/copaw/app/agent_context.py | Adds helper to resolve workspace for a request (header/path/active agent). |
| src/copaw/app/_app.py | Reworks app startup to migrate/init multi-agent manager + dynamic runner + mounts agent-scoped router. |
| src/copaw/agents/tools/shell.py | Resolves shell tool cwd via workspace ContextVar fallback to WORKING_DIR. |
| src/copaw/agents/tools/file_search.py | Resolves search root via workspace ContextVar fallback to WORKING_DIR. |
| src/copaw/agents/tools/file_io.py | Resolves relative file paths via workspace ContextVar fallback to WORKING_DIR. |
| src/copaw/agents/skills_manager.py | Refactors skill management to be workspace-dir scoped (SkillService becomes instance-based). |
| src/copaw/agents/skills_hub.py | Updates skill hub install to write/enable skills in a specific workspace. |
| src/copaw/agents/react_agent.py | Makes agent runtime use agent config + workspace dir for prompt/skills/tools/memory compaction hooks. |
| src/copaw/agents/prompt.py | Allows system prompt builder to read from a specified working/workspace directory. |
| src/copaw/agents/memory/memory_manager.py | Adds configurable memory behavior, reduces reliance on global config, and attempts degraded mode when reme missing. |
| src/copaw/agents/hooks/memory_compaction.py | Makes compaction thresholds explicit inputs instead of reading global config. |
| src/copaw/agents/command_handler.py | Makes history token limits explicit input rather than reading global config. |
| console/src/stores/agentStore.ts | Adds persisted Zustand store for active agent + agent list. |
| console/src/pages/Settings/Models/index.tsx | Simplifies models page layout (removes ModelsSection). |
| console/src/pages/Settings/Agents/index.tsx | Adds Agent Management settings page (CRUD + activation). |
| console/src/pages/Settings/Agents/index.module.less | Styles for agent management page. |
| console/src/pages/Control/Heartbeat/index.tsx | Reloads heartbeat settings when active agent changes. |
| console/src/pages/Control/CronJobs/useCronJobs.ts | Reloads cron jobs when active agent changes. |
| console/src/pages/Control/Channels/useChannels.ts | Reloads channel config when active agent changes. |
| console/src/pages/Chat/index.tsx | Adds agent switching refresh + propagates X-Agent-Id in chat request. |
| console/src/pages/Agent/Workspace/components/useAgentsData.ts | Uses agent-specific endpoints to list/read workspace files. |
| console/src/pages/Agent/Tools/useTools.ts | Reloads tools on agent change + optimistic toggling behavior. |
| console/src/pages/Agent/Skills/useSkills.ts | Reloads skills when active agent changes. |
| console/src/pages/Agent/MCP/useMCP.ts | Reloads MCP clients when active agent changes. |
| console/src/pages/Agent/MCP/index.tsx | Minor TS cleanup (const for clientsToCreate). |
| console/src/locales/zh.json | Adds agent-management i18n strings and common table strings. |
| console/src/locales/en.json | Adds agent-management i18n strings and common table strings. |
| console/src/layouts/Sidebar.tsx | Adds “Agent Management” nav item. |
| console/src/layouts/MainLayout/index.tsx | Adds /agents route. |
| console/src/layouts/Header.tsx | Adds AgentSelector to header. |
| console/src/components/AgentSelector/index.tsx | Adds agent switcher dropdown that activates agents and updates store. |
| console/src/components/AgentSelector/index.module.less | Styles for agent selector. |
| console/src/api/types/index.ts | Exports new agents API types. |
| console/src/api/types/agents.ts | Adds typed contracts for agents API responses. |
| console/src/api/request.ts | Adds global X-Agent-Id header injection based on agent store persistence. |
| console/src/api/modules/agents.ts | Adds agents API client module. |
| console/src/api/index.ts | Exposes agentsApi. |
| SKILL_MULTI_WORKSPACE_FIX.md | Notes/guide describing the skills multi-workspace refactor. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a fundamental architectural shift towards a multi-agent system, allowing for robust isolation and independent operation of multiple AI agents. The changes enable each agent to maintain its own distinct environment, configurations, and operational data, significantly enhancing the platform's capability for multi-tenancy and specialized agent deployments. The refactoring touches both the backend API and the frontend user interface to support this new paradigm, while also providing a migration path for existing single-agent setups. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces comprehensive multi-agent support by refactoring the backend to manage agent-specific workspaces, each with its own runner, channel, memory, MCP, and cron managers. Key changes include making SkillService an instance class bound to a workspace, updating various agent components and API routes to use workspace-specific directories and configurations, and implementing a MultiAgentManager for lazy loading and lifecycle management of agent instances. The frontend is updated to include an agent selector, new API endpoints for agent management, and ensures that all requests are routed with the active agent's ID. A migration path for legacy single-agent configurations to the new multi-agent structure is also included. A review comment highlights a code duplication issue in the frontend where logic for retrieving the active agent ID from localStorage is duplicated and suggests extracting it into a shared utility function for improved maintainability and consistency.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 64 out of 64 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Thanks for this monumental effort This multi-agent/multi-workspace architecture is exactly what the project needs to evolve. The focus on true filesystem-level isolation and multi-tenancy is impressive and highly appreciated. It’s a huge architectural improvement that makes Copaw much more robust for complex use cases. Looking forward to seeing this merged! |
CoPaw Multi-Agent Architecture Refactor – Technical OverviewWhy the Refactor?The previous architecture was single-agent with all configuration crammed into a root
Refactor goal: each agent should have its own fully isolated workspace. Core Design: Workspace IsolationNew Directory Layout~/.copaw/
├── config.json # global config only (providers, active_agent)
└── workspaces/
├── default/
│ ├── agent.json # per-agent configuration
│ ├── chats.json
│ ├── jobs.json
│ ├── AGENTS.md
│ └── skills/
└── agent2/
└── ...All agent-specific config previously in the root (channels, MCP, tools, system_prompt, etc.) is now moved into
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 98 out of 98 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Normalize the path: expand ~ and fix Unicode normalization differences | ||
| # (e.g. macOS stores filenames as NFD but paths from the LLM arrive as NFC, | ||
| # causing os.path.exists to return False for files that do exist). | ||
| file_path = os.path.expanduser(unicodedata.normalize("NFC", file_path)) | ||
|
|
||
| if not os.path.exists(file_path): | ||
| return ToolResponse( | ||
| content=[ | ||
| TextBlock( | ||
| type="text", | ||
| text=f"Error: The file {file_path} does not exist.", | ||
| ), | ||
| ], | ||
| ) | ||
|
|
||
| if not os.path.isfile(file_path): | ||
| return ToolResponse( | ||
| content=[ | ||
| TextBlock( | ||
| type="text", | ||
| text=f"Error: The path {file_path} is not a file.", | ||
| ), | ||
| ], | ||
| ) | ||
|
|
||
| # Detect MIME type | ||
| mime_type, _ = mimetypes.guess_type(file_path) | ||
| if mime_type is None: | ||
| # Default to application/octet-stream for unknown types | ||
| mime_type = "application/octet-stream" | ||
| as_type = _auto_as_type(mime_type) | ||
|
|
||
| try: | ||
| # Use local file URL instead of base64 | ||
| absolute_path = os.path.abspath(file_path) | ||
|
|
||
| if not _is_allowed_media_path(absolute_path): | ||
| return ToolResponse( | ||
| content=[ | ||
| TextBlock( | ||
| type="text", | ||
| text=f"Error: Media file outside allowed directory: {os.path.basename(file_path)}", | ||
| ), | ||
| ], | ||
| ) | ||
|
|
||
| file_url = f"file://{absolute_path}" | ||
| source = {"type": "url", "url": file_url} | ||
|
|
| try: | ||
| workspace = await manager.get_agent(target_agent_id) | ||
| if not workspace: | ||
| raise HTTPException( | ||
| status_code=404, | ||
| detail=f"Agent '{target_agent_id}' not found", | ||
| ) | ||
| return workspace | ||
| except Exception as e: | ||
| raise HTTPException( | ||
| status_code=500, | ||
| detail=f"Failed to get agent: {str(e)}", | ||
| ) from e |
| import logging | ||
| from ..agent_context import set_current_agent_id | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
| agent_id = None | ||
|
|
||
| # Priority 1: Extract agentId from path: /api/agents/{agentId}/... | ||
| path_parts = request.url.path.split("/") | ||
| if len(path_parts) >= 4 and path_parts[1] == "api": | ||
| if path_parts[2] == "agents": | ||
| agent_id = path_parts[3] | ||
| request.state.agent_id = agent_id | ||
| logger.debug( | ||
| f"AgentContextMiddleware: agent_id={agent_id} " | ||
| f"from path={request.url.path}", | ||
| ) | ||
|
|
||
| # Priority 2: Check X-Agent-Id header | ||
| if not agent_id: | ||
| agent_id = request.headers.get("X-Agent-Id") | ||
| if agent_id: | ||
| logger.debug( | ||
| f"AgentContextMiddleware: agent_id={agent_id} " | ||
| f"from X-Agent-Id header", | ||
| ) | ||
|
|
||
| # Set agent_id in context variable for use by runners | ||
| if agent_id: | ||
| set_current_agent_id(agent_id) | ||
|
|
||
| response = await call_next(request) | ||
| return response |
| with zipfile.ZipFile(io.BytesIO(data)) as zf: | ||
| for name in zf.namelist(): | ||
| resolved = (WORKING_DIR / name).resolve() | ||
| if not str(resolved).startswith(str(WORKING_DIR)): | ||
| resolved = (workspace_dir / name).resolve() | ||
| if not str(resolved).startswith(str(workspace_dir)): | ||
| raise HTTPException( | ||
| status_code=400, | ||
| detail=f"Zip contains unsafe path: {name}", | ||
| ) |
| @router.patch("/{tool_name}/toggle", response_model=ToolInfo) | ||
| async def toggle_tool(tool_name: str = Path(...)) -> ToolInfo: | ||
| """Toggle tool enabled status. | ||
| async def toggle_tool( | ||
| tool_name: str = Path(...), | ||
| request: Request = None, | ||
| ) -> ToolInfo: | ||
| """Toggle tool enabled status for active agent. |
| @@ -48,33 +66,61 @@ async def list_tools() -> List[ToolInfo]: | |||
|
|
|||
|
|
|||
| @router.patch("/{tool_name}/toggle", response_model=ToolInfo) | |||
| async def toggle_tool(tool_name: str = Path(...)) -> ToolInfo: | |||
| """Toggle tool enabled status. | |||
| async def toggle_tool( | |||
| tool_name: str = Path(...), | |||
| request: Request = None, | |||
| ) -> ToolInfo: | |||
| """Toggle tool enabled status for active agent. | |||
|
|
|||
| Args: | |||
| tool_name: Tool function name | |||
| request: FastAPI request | |||
|
|
|||
| Returns: | |||
| Updated tool information | |||
|
|
|||
| Raises: | |||
| HTTPException: If tool not found | |||
| """ | |||
| config = load_config() | |||
| from ..agent_context import get_agent_for_request | |||
| from ...config.config import load_agent_config, save_agent_config | |||
|
|
|||
| workspace = await get_agent_for_request(request) | |||
| agent_config = load_agent_config(workspace.agent_id) | |||
|
|
|||
| if tool_name not in config.tools.builtin_tools: | |||
| if ( | |||
| not agent_config.tools | |||
| or tool_name not in agent_config.tools.builtin_tools | |||
| ): | |||
| raise HTTPException( | |||
| status_code=404, | |||
| detail=f"Tool '{tool_name}' not found", | |||
| ) | |||
|
|
|||
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 102 out of 102 changed files in this pull request and generated 9 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| **多智能体支持:** 所有命令都支持 `--agent-id` 参数(默认为 `default`)。 | ||
|
|
||
| ```bash |
| """Set active model for current agent.""" | ||
| # Validate provider and model exist | ||
| try: | ||
| await manager.activate_model(body.provider_id, body.model) | ||
| except ValueError as exc: | ||
| message = str(exc) | ||
| lower_msg = message.lower() | ||
| if "provider" in lower_msg and "not found" in lower_msg: | ||
| # Missing provider | ||
| raise HTTPException(status_code=404, detail=message) from exc | ||
| # Invalid model, unreachable provider, or other configuration error | ||
| raise HTTPException(status_code=400, detail=message) from exc | ||
|
|
||
| # Save to agent config | ||
| try: | ||
| workspace = await get_agent_for_request(request) | ||
| agent_config = load_agent_config(workspace.agent_id) | ||
| agent_config.active_model = ModelSlotConfig( | ||
| provider_id=body.provider_id, | ||
| model=body.model, | ||
| ) | ||
| save_agent_config(workspace.agent_id, agent_config) | ||
| except Exception as e: | ||
| # Log warning but don't fail the request | ||
| logger.warning( | ||
| f"Failed to save active model to agent config: {e}", | ||
| ) | ||
|
|
||
| return ActiveModelsInfo(active_llm=manager.get_active_model()) |
| # Get default workspace path for subsequent operations | ||
| default_workspace = Path("~/.copaw/workspaces/default").expanduser() | ||
|
|
| self._media_dir = ( | ||
| Path(media_dir).expanduser() if media_dir else _DEFAULT_MEDIA_DIR | ||
| ) | ||
| self._workspace_dir = ( | ||
| Path(workspace_dir).expanduser() if workspace_dir else None | ||
| ) |
| **Multi-Agent Support:** All commands support the `--agent-id` parameter (defaults to `default`). | ||
|
|
||
| ```bash |
| @router.patch("/{tool_name}/toggle", response_model=ToolInfo) | ||
| async def toggle_tool(tool_name: str = Path(...)) -> ToolInfo: | ||
| """Toggle tool enabled status. | ||
| async def toggle_tool( | ||
| tool_name: str = Path(...), | ||
| request: Request = None, | ||
| ) -> ToolInfo: | ||
| """Toggle tool enabled status for active agent. | ||
|
|
||
| Args: | ||
| tool_name: Tool function name | ||
| request: FastAPI request | ||
|
|
||
| Returns: | ||
| Updated tool information | ||
|
|
||
| Raises: | ||
| HTTPException: If tool not found | ||
| """ | ||
| config = load_config() | ||
| from ..agent_context import get_agent_for_request | ||
| from ...config.config import load_agent_config, save_agent_config | ||
|
|
||
| workspace = await get_agent_for_request(request) | ||
| agent_config = load_agent_config(workspace.agent_id) | ||
|
|
||
| if tool_name not in config.tools.builtin_tools: | ||
| if ( | ||
| not agent_config.tools | ||
| or tool_name not in agent_config.tools.builtin_tools | ||
| ): | ||
| raise HTTPException( | ||
| status_code=404, | ||
| detail=f"Tool '{tool_name}' not found", | ||
| ) | ||
|
|
||
| # Toggle enabled status | ||
| tool_config = config.tools.builtin_tools[tool_name] | ||
| tool_config = agent_config.tools.builtin_tools[tool_name] | ||
| tool_config.enabled = not tool_config.enabled | ||
|
|
||
| # Save config | ||
| save_config(config) | ||
| # Save agent config | ||
| save_agent_config(workspace.agent_id, agent_config) | ||
|
|
||
| # Hot reload config (async, non-blocking) | ||
| import asyncio | ||
|
|
||
| async def reload_in_background(): | ||
| try: | ||
| manager = request.app.state.multi_agent_manager | ||
| await manager.reload_agent(workspace.agent_id) |
| try: | ||
| # Use local file URL instead of base64 | ||
| absolute_path = os.path.abspath(file_path) | ||
|
|
||
| if not _is_allowed_media_path(absolute_path): | ||
| return ToolResponse( | ||
| content=[ | ||
| TextBlock( | ||
| type="text", | ||
| text=f"Error: Media file outside allowed directory: {os.path.basename(file_path)}", | ||
| ), | ||
| ], | ||
| ) | ||
|
|
||
| file_url = f"file://{absolute_path}" | ||
| source = {"type": "url", "url": file_url} | ||
|
|
| try: | ||
| workspace = await manager.get_agent(target_agent_id) | ||
| if not workspace: | ||
| raise HTTPException( | ||
| status_code=404, | ||
| detail=f"Agent '{target_agent_id}' not found", | ||
| ) | ||
| return workspace | ||
| except Exception as e: | ||
| raise HTTPException( | ||
| status_code=500, | ||
| detail=f"Failed to get agent: {str(e)}", | ||
| ) from e |
| for name in zf.namelist(): | ||
| resolved = (WORKING_DIR / name).resolve() | ||
| if not str(resolved).startswith(str(WORKING_DIR)): | ||
| resolved = (workspace_dir / name).resolve() | ||
| if not str(resolved).startswith(str(workspace_dir)): | ||
| raise HTTPException( | ||
| status_code=400, | ||
| detail=f"Zip contains unsafe path: {name}", | ||
| ) |
Multi-Agent/Multi-Workspace Architecture
Description
This PR introduces a complete multi-agent/multi-workspace architecture that enables running multiple isolated AI agents simultaneously, each with their own configuration, memory, skills, and tools. This is a significant architectural overhaul to support true multi-tenancy at the agent level.
🚨 Breaking Changes
Configuration Structure:
config.jsonstructure has changed significantlyagentssection now contains only references (profiles) to workspace directories~/.copaw/workspaces/{agent_id}/agent.jsonData Directory Layout:
This is a work-in-progress preview for community feedback. The following are NOT yet implemented:
❌ CLI Commands: MostcopawCLI commands still operate on global/default agent onlycopaw init- needs update for multi-agent setupcopaw chat- needs agent selection flagcopaw skills,copaw channels,copaw cron- need agent contextcopaw daemon- partially updated but needs refinementBulk agent creation/importAgent templates/presetsAdvanced workspace management🎯 What's Working
Related Issue: N/A (new feature)
Security Considerations:
Type of Change
Component(s) Affected
Checklist
pre-commit run --all-fileslocally and it passespytestor as relevant) and they passTesting
Manual Testing Done
write_file,read_file,execute_shell_commanduse correct workspacechats.jsonWhat Needs Testing
Local Verification Evidence
Additional Notes
Why Release This Now?
This PR represents days of architectural work and we want to:
Migration Path
Users on
mainbranch will need to:~/.copaw/directory~/.copaw/workspaces/default/agent.jsonWhat's Next
Priority TODO list:
--agent-idflagThis is a preview release. Please test in a non-production environment first. Feedback, bug reports, and contributions are highly appreciated! 🙏