-
Notifications
You must be signed in to change notification settings - Fork 32
🤖 feat: sub-workspaces as subagents #1219
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
ce6d41b to
1c1ae0d
Compare
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
106c139 to
943b4e6
Compare
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@codex review |
|
Codex Review: Didn't find any major issues. You're on a roll. ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
61a814b to
846aa28
Compare
8e70272 to
deda02a
Compare
deda02a to
00b267a
Compare
Change-Id: I98401f98f52a9ba82adc854ef796fa7da0494553 Signed-off-by: Thomas Kosiewski <tk@coder.com>
00b267a to
a045958
Compare
Implements “sub-workspaces as subagents” by introducing agent Tasks backed by child workspaces spawned via the new
tasktool.research,exploreagent_report+ leaf auto-cleanupValidation:
make static-checkbun test src/node/services/tools/task.test.ts src/node/services/taskService.test.ts📋 Implementation Plan
🤖 Sub-workspaces as subagents (Mux)
Decisions (confirmed)
runtimeConfig); preferruntime.forkWorkspace(...)(when implemented) so the child starts from the parent’s branch.agent_reportand we post the report back into the parent workspace.tasktool so any agent workspace can spawn sub-agent tasks (depth-limited).Recommended approach: Workspace Tasks (net +~1700 LoC product code)
Represent each subagent as a Task (as described in
subagents.md), implemented as a child workspace plus orchestration.This keeps the v1 scope small while keeping the API surface task-shaped so we can later reuse it for non-agent tasks (e.g., background bashes).
High-level architecture
Data model
Alignment with
subagents.md(what we’re matching)agentIdmaps cleanly to Mux’sworkspaceIdfor the child workspace.Task(subagent_type=…, prompt=…)becomes Mux tooltask, backed byTask.create({ parentWorkspaceId, kind: "agent", agentType, prompt }).tools/disallowedToolsmaps to Mux’s existingtoolPolicy(applied in order).agent_reporttool call (child → parent) plus a backend retry if the tool wasn’t called. (Future: bash tasks can map to existing background bash output, or be unified behind aTask.outputAPI.)task({ run_in_background: true, ... })returns immediately; otherwise the tool blocks until the child callsagent_report(with a timeout).Extend workspace metadata with optional fields:
parentWorkspaceId?: string— enables nesting in the UIagentType?: "research" | "explore" | string— selects an agent preset(These are optional so existing configs stay valid.)
Agent presets (built-in)
Create a small registry of agent presets that define:
toolPolicy(enforced)systemPrompt(preset-defined; can replace or append; v1 uses replace so each subagent can fully override the parent’s user instructions)Implementation detail: for agent task workspaces, treat the preset’s
systemPromptas the effective prompt (internal mode), instead of always appending to the parent workspace’s system message.agent_reportexactly once when it has a final answerInitial presets:
web_search+web_fetch(and optionallyfile_read), disallow edits.file_read+bashforrg/git), disallow file edits.Both presets should enable:
task(so agents can spawn subagents when useful)agent_report(so leaf tasks have a single, explicit channel for reporting back)Enforce max nesting depth from settings (default 3) in the backend to prevent runaway recursion.
Implementation steps
1) Schemas + types (IPC boundary)
Net +~50 LoC
WorkspaceMetadataSchema/FrontendWorkspaceMetadataSchema(src/common/orpc/schemas/workspace.ts)WorkspaceConfigSchema(src/common/orpc/schemas/project.ts)WorkspaceMetadata/FrontendWorkspaceMetadatatypes.2) Persist config (workspace tree + task settings)
Net +~320 LoC
Workspace tree fields
parentWorkspaceIdandagentType.Config.getAllWorkspaceMetadata()to include the new fields when constructing metadata.Task settings (global; shown in Settings UI)
taskSettingsin~/.mux/config.json, e.g.:maxParallelAgentTasks(default 3)maxTaskNestingDepth(default 3)api.config.getConfig(); persist viaapi.config.saveConfig().Task durability fields (per agent task workspace)
taskStatus: queued|running|awaiting_report) so we can rehydrate and resume after restart.3) Backend Task API: Task.create
Net +~450 LoC
Add a new task operation (ORPC + service) that is intentionally generic:
Task.create({ parentWorkspaceId, kind, ... }){ taskId, kind, status }.V1: implement
kind: "agent"(sub-workspace agent task):taskSettings(configurable):maxTaskNestingDepth, default 3) by walking theparentWorkspaceIdchain.maxParallelAgentTasks, default 3) by counting running agent tasks globally (across the app).status: "queued"and start later (FIFO).agent_research_<id>; must match[a-z0-9_-]{1,64}).runtimeConfig(Local/Worktree/SSH).runtime.forkWorkspace(...)(when implemented) so the child starts from the parent’s branch.runtime.createWorkspace(...)with the same runtime config (no branch isolation).{ parentWorkspaceId, agentType, taskStatus }.Durability / restart:
On app startup, rehydrate queued/running tasks from config and resume them:
maxParallelAgentTasksParent await semantics (restart-safe):
queued|running|awaiting_report, treat it as “awaiting” and avoid starting new subagent tasks from it.tasktool call.Design note: keep the return type “task-shaped” (e.g.,
{ taskId, kind, status }) so we can later addkind: "bash"tasks that wrap existing background bashes.4) Tool:
task(agents can spawn sub-agents)Net +~250 LoC
Expose a Claude-like
Tasktool to the LLM (but backed by Mux workspaces):Tool:
taskInput (v1):
{ subagent_type: string, prompt: string, description?: string, run_in_background?: boolean }Behavior:
Spawn (or enqueue) a child agent task via
Task.create({ parentWorkspaceId: <current workspaceId>, kind: "agent", agentType: subagent_type, prompt, ... }).If
run_in_backgroundis true: return immediately{ status: "queued" | "running", taskId }.Otherwise: block (potentially across queue + execution) until the child calls
agent_report(or timeout) and return{ status: "completed", reportMarkdown }.Durability: if this foreground wait is interrupted (app restart), the child task continues; when it reports, we persist the tool output into the parent message and auto-resume the parent stream.
Wire-up: add to
TOOL_DEFINITIONS+ register ingetToolsForModel(); injecttaskServiceinto ToolConfiguration so the tool can callTask.create.Guardrails
maxTaskNestingDepthandmaxParallelAgentTasksfrom settings (defaults: depth 3, parallel 3).agent_report.5) Enforce preset tool policy + system prompt
Net +~130 LoC
In the backend send/stream path:
effectivePolicy = [...(options.toolPolicy ?? []), ...presetPolicy]systemPromptas the effective instructions (internal-only agent mode)."agent") that starts from an empty base prompt (no user custom instructions), then applypreset.systemPrompt.tasktool (availablesubagent_types).agent_report(final answer only; after any spawned tasks complete).6) Auto-report back + auto-delete (orchestrator)
Net +~450 LoC
Add a small reporting tool + orchestrator that ensures the child reports back explicitly, and make it durable across restarts.
Tool:
agent_report{ reportMarkdown: string, title?: string }(or similar){ success: true }(the backend uses the tool-call args as the report payload)TOOL_DEFINITIONS+ register ingetToolsForModel()as a non-runtime toolOrchestrator behavior
Primary path: handle
tool-call-endforagent_reportworkspaceIdis an agent task workspace and hasparentWorkspaceId.taskStatus: "reported"(+reportedAt).tasktool call, update that tool part frominput-available→output-availablewith{ reportMarkdown, title }(like theask_user_questionrestart-safe fallback).tool-call-end+workspace.onChatevents so the UI updates immediately.workspace.resumeStream(parent)after writing the tool output.Enforcement path: if a stream ends without an
agent_reportcallagent_report.7) UI: nested sidebar rows
Net +~100 LoC
depthprop toWorkspaceListItemand adjust left padding.8) No user-facing launcher (agent-orchestrated only)
Net +~0 LoC
tasktool from the parent workspace.9) Tests
~200 LoC tests (not counted in product LoC estimate)
tasktool spawns/enqueues a child agent task and enforcesmaxTaskNestingDepth.maxParallelAgentTasks(extra tasks stay queued until a slot frees).agent_reportposts report to parent, updates waitingtasktool output (restart-safe), and triggers cleanup (and reminder path when missing).Follow-ups (explicitly out of scope for v1)
Task.create(kind: "bash")tasks that wrap existing background bashes (and optionally render under the parent like agent tasks).bashto Explore.Generated with
codex cli• Model:gpt-5.2• Thinking:xhigh