diff --git a/docs/chatgpt-coding-workflow.md b/docs/chatgpt-coding-workflow.md index 93e7e418..1b275ce1 100644 --- a/docs/chatgpt-coding-workflow.md +++ b/docs/chatgpt-coding-workflow.md @@ -93,8 +93,9 @@ setup installs in `~/.devspace/skills` when agent tooling is enabled, plus: When agent tooling is enabled, DevSpace discovers agent profiles from `~/.devspace/agents/*.md` and project `.devspace/agents/*.md`. -`open_workspace` exposes only usable provider names and profile names with -descriptions. Disabled or unavailable providers and their profiles are omitted. +`open_workspace` exposes only usable provider capability hints and profiles with +their provider and optional model/effort defaults. Disabled or unavailable +providers and their profiles are omitted. Example profiles are packaged under `examples/agents/` for users who want starter templates. Copy or adapt them into one of the active profile directories diff --git a/docs/configuration.md b/docs/configuration.md index bc5277d0..59bb47c5 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -119,8 +119,8 @@ When agent tooling is enabled, DevSpace discovers agent profiles from: - `~/.devspace/agents/*.md` - project `.devspace/agents/*.md` -`open_workspace` returns only usable provider names and profile names with -descriptions. `devspace agents ls` lists existing subagent sessions for the +`open_workspace` returns only usable provider capability hints and profiles with +their provider and optional model/effort defaults. `devspace agents ls` lists existing subagent sessions for the current workspace, scoped by the workspace environment injected into shell commands. The `subagents` skill teaches the model to discover targets with `devspace agents targets`, then use the minimal `devspace agents run`, diff --git a/docs/dynamic-workflows.md b/docs/dynamic-workflows.md index 8020b1ac..734b2e5c 100644 --- a/docs/dynamic-workflows.md +++ b/docs/dynamic-workflows.md @@ -73,9 +73,25 @@ When agent tooling is enabled, `open_workspace` stays deliberately small: ```json { - "agentProviders": ["codex", "claude"], + "agentProviders": [ + { + "name": "codex", + "model": { "supported": true, "discovery": "model_dependent" }, + "effort": { + "supported": true, + "semantics": "reasoning_effort", + "discovery": "model_dependent" + } + } + ], "agents": [ - { "name": "reviewer", "description": "Review changes and test gaps." } + { + "name": "reviewer", + "description": "Review changes and test gaps.", + "provider": "codex", + "model": "gpt-5.4", + "effort": "high" + } ], "activeWorkflows": [ { @@ -88,7 +104,9 @@ When agent tooling is enabled, `open_workspace` stays deliberately small: } ``` -Provider capability metadata, models, effort semantics, session identifiers, -workflow phases, and internal counters are intentionally absent. Models obtain -execution details only when needed through `devspace agents targets --json` or -the workflow inspection commands. +Provider entries contain compact model and effort capability hints. Profiles +expose only their name, description, provider, and optional model/effort +defaults. Unavailable or unselected providers and profiles are omitted. +Workflow entries keep only the run id, name, live status, and call counters +needed to decide whether to inspect or poll; detailed calls remain available +through the CLI inspection commands. diff --git a/skills/dynamic-workflows/SKILL.md b/skills/dynamic-workflows/SKILL.md index e20273c8..374cf19b 100644 --- a/skills/dynamic-workflows/SKILL.md +++ b/skills/dynamic-workflows/SKILL.md @@ -1,13 +1,13 @@ --- name: dynamic-workflows -description: Create and run resumable multi-agent orchestration with the DevSpace CLI. Use when work needs programmed fan-out, multiple phases, per-item pipelines, structured aggregation, isolated parallel writers, or recovery after a failed workflow; use a direct subagent for one bounded delegation. +description: Create and run resumable multi-agent workflows with the DevSpace CLI. Use for programmed fan-out, dependent stages, per-item processing, structured aggregation, isolated parallel work, or recovery after a failed run; use a direct subagent for one bounded delegation. --- -# DevSpace Dynamic Workflows +# DevSpace dynamic workflows -Use the DevSpace CLI through the host's shell or process tool. Run commands from the project the workflow should operate on. DevSpace scopes runs to the host workspace when supplied, otherwise to the current Git repository or project directory. - -Prefer `--json` from an agent harness: it starts or inspects work without holding one tool call open. Retain the returned workflow id and poll explicitly. Use `--follow` only when streaming output is useful and the shell tool supports a long-running process. Do not combine `--json` and `--follow`. +Use the DevSpace CLI from the project the workflow should operate on. Prefer +JSON output from a coding harness so each command returns promptly and the +harness can poll by id. ## Run and inspect @@ -21,13 +21,16 @@ devspace workflow cancel --json devspace workflow ls --json ``` -Named workflows live at `.devspace/workflows/.js`. `--script-path` is an alias for `--file`. `--arg key=value` accepts repeated run inputs through the script's `args` value. - -Poll `status --json` until the workflow reaches `completed`, `failed`, or `cancelled`. Use `calls` for the compact child-call list and `call` for one call's prompt, result, or error. +Named workflows are project files at `.devspace/workflows/.js`. +`--script-path` is an alias for `--file`; repeat `--arg key=value` to pass +inputs. Poll `status` until the run is `completed`, `failed`, or `cancelled`. +Use `calls` for the compact child-call list and `call` for one call's details. +Use `--follow` instead of `--json` when a long-running shell can stream output. ## Write a workflow -The first executable statement must export literal metadata. The script then uses the provided orchestration primitives and returns a JSON-compatible result. +Export literal metadata, then compose the available primitives. Return a +JSON-compatible value. ```js export const meta = { @@ -52,54 +55,45 @@ const summary = await agent( return { findings, summary } ``` -Available primitives: - -- `agent(prompt, options?)` delegates one bounded task. Options are `label`, `phase`, `schema`, `profile`, `provider`, `model`, `effort`, and `isolation: 'worktree'`. `profile` and `provider` are mutually exclusive. -- `parallel([thunks])` runs independent tasks concurrently and preserves input order. A failed branch produces `null` in its slot. -- `pipeline(items, ...stages)` processes each item through dependent stages; failed item chains produce `null` without stopping unrelated items. -- `phase(title)` and `log(message)` record meaningful progress. -- `workflow(nameOrRef, args?)` composes another named workflow or `{ scriptPath }` one level deep. -- `args` contains values passed with `--arg`. +Primitives and options: -Use `devspace agents targets --json` before choosing a profile or provider. Prefer profiles for reusable role instructions and defaults. Only pass model or effort overrides when their exact values are already known. +- `agent(prompt, options?)` delegates one task. Options are `label`, `phase`, + `schema`, `profile`, `provider`, `model`, `effort`, and + `isolation: 'worktree'`. Choose either `profile` or `provider`. +- `parallel([thunks])` runs independent tasks concurrently and keeps input + order. `pipeline(items, ...stages)` runs dependent stages for each item. +- `phase(title)` and `log(message)` record useful progress. +- `workflow(nameOrRef, args?)` composes another workflow one level deep. +- `args` contains values supplied with `--arg`. -Use `schema` when later workflow steps need typed JSON rather than prose: - -```js -const review = await agent('Return the discovered bugs.', { - schema: { - type: 'object', - properties: { - bugs: { type: 'array', items: { type: 'string' } }, - }, - required: ['bugs'], - }, -}) -``` +Use `devspace agents targets --json` before choosing a profile or provider. +Profiles provide reusable role instructions and defaults. Use worktree +isolation for parallel writers that could touch the same files; shared +isolation is suitable for readers or intentionally sequential writers. +Use `schema` when a later stage needs structured JSON. -Use `isolation: 'worktree'` for parallel agents that may modify overlapping checkouts. Shared isolation is appropriate for readers or intentionally sequential writers. +## Common patterns -Workflow scripts must be replayable: do not use `Date.now()`, `Math.random()`, or `new Date()` without an argument. Pass changing values through `args`. +- Fan out correctness, security, and test reviews, then ask one agent to + combine the findings. +- Process a list of files through analysis, implementation, and verification + stages. +- Run competing implementations in isolated worktrees and compare their + results before choosing one. -## Recover a run +## Resume a run -Failed and cancelled runs are terminal. Inspect the prior run, fix or replace its script, then create a resumed run: +Failed or cancelled runs can be resumed after fixing the workflow or its +inputs: ```bash devspace workflow status --json -devspace workflow calls --json -devspace workflow call --json devspace workflow run --resume --json devspace workflow run --resume --file --json ``` -Keep completed calls' prompts and options stable when their results should be reused. Resume reuses the unchanged successful prefix and executes from the first call that failed, changed, or cannot be reused. - -A completed `isolation: 'worktree'` call cannot be reused because its checkout is not restored. When resume reaches one, that call and every later call execute again, even if their inputs are unchanged. Do not assume mutations from the prior isolated checkout are present in the resumed run. - -## Good uses +Inspect `status`, `calls`, and individual `call` results before resuming. +Keep the same profile/provider and prompt for stages whose earlier results +should be reused. -- Fan out a change review across correctness, security, and tests, then synthesize it. -- Analyze many files with the same staged pipeline. -- Run parallel implementations in isolated worktrees and compare their results. -- Encode a repeatable migrate, review, and verify sequence. +Use a direct `devspace agents run` command for one independent delegation. diff --git a/skills/subagents/SKILL.md b/skills/subagents/SKILL.md index 53ab44d6..8b7ce452 100644 --- a/skills/subagents/SKILL.md +++ b/skills/subagents/SKILL.md @@ -19,6 +19,11 @@ Prefer a configured profile whose description matches the task. Use a provider t Profiles carry their own provider, instructions, model, and effort defaults. Only pass `--model` or `--effort` when the user supplied an exact value or the value is already known to be valid for that target. +`--model ` selects a provider model. `--effort ` selects the +provider's reasoning/thinking level (`--thinking` is an alias). Both are +optional; use values reported by `agents targets --json` or a configured +profile. + ## Start work Give the child a self-contained brief. Include the objective, relevant paths, constraints, decisions from the parent conversation, and the expected result. A child cannot see the parent conversation or ask the user for missing context. @@ -42,6 +47,9 @@ devspace agents ls --json - `run ` continues the same agent session with a new prompt. - `ls` returns sessions belonging to the current project. +Use `--json` on every command when the calling harness needs machine-readable +ids, status, responses, or errors. + Poll `show --json` while the status is `starting` or `running`. `idle` means the response is ready; `error` and `stopped` are terminal without a successful response. Use a continuation only when the same context is valuable; start a new subagent for independent work. ## Good uses diff --git a/src/local-agent-resolution.ts b/src/local-agent-resolution.ts index 3ab67e1d..5f817a6d 100644 --- a/src/local-agent-resolution.ts +++ b/src/local-agent-resolution.ts @@ -73,7 +73,10 @@ export function resolveLocalAgentExecution( if (input.profile) { const profile = input.profiles.find((candidate) => candidate.name === input.profile); if (!profile) { - const available = input.profiles.map((candidate) => candidate.name).join(", "); + const available = input.profiles + .filter((candidate) => input.availableProviders.includes(candidate.provider)) + .map((candidate) => candidate.name) + .join(", "); throw new LocalAgentResolutionError( "profile_not_found", `Unknown agent profile: ${input.profile}${available ? `. Available profiles: ${available}` : ""}`, diff --git a/src/local-agent-targets.test.ts b/src/local-agent-targets.test.ts index e4b0f534..9d282e46 100644 --- a/src/local-agent-targets.test.ts +++ b/src/local-agent-targets.test.ts @@ -143,4 +143,6 @@ assert.throws( assert.equal(resolveLocalAgentTarget("missing", profiles), undefined); assert.match(formatAvailableLocalAgentTargets(profiles), /profiles: reviewer, claude/); +assert.match(formatAvailableLocalAgentTargets(profiles, ["codex"]), /profiles: reviewer/); +assert.doesNotMatch(formatAvailableLocalAgentTargets(profiles, ["codex"]), /claude/); assert.match(formatAvailableLocalAgentTargets([]), /providers: codex, claude, opencode, pi, cursor, copilot/); diff --git a/src/local-agent-targets.ts b/src/local-agent-targets.ts index 2b801aec..6c3df35f 100644 --- a/src/local-agent-targets.ts +++ b/src/local-agent-targets.ts @@ -103,7 +103,10 @@ export function formatAvailableLocalAgentTargets( profiles: LocalAgentProfile[], providers: LocalAgentProvider[] = [...LOCAL_AGENT_PROVIDERS], ): string { - const profileNames = profiles.map((profile) => profile.name); + const availableProviders = new Set(providers); + const profileNames = profiles + .filter((profile) => availableProviders.has(profile.provider)) + .map((profile) => profile.name); const parts = [ profileNames.length > 0 ? `profiles: ${profileNames.join(", ")}` : undefined, providers.length > 0 ? `providers: ${providers.join(", ")}` : "providers: none", diff --git a/src/open-workspace-capabilities.test.ts b/src/open-workspace-capabilities.test.ts index a2e9b2e3..a1fb4bf1 100644 --- a/src/open-workspace-capabilities.test.ts +++ b/src/open-workspace-capabilities.test.ts @@ -52,8 +52,22 @@ const parsed = enabledSchema.parse({ agentsFiles: [], availableAgentsFiles: [], skills: [], - agentProviders: ["codex"], - agents: [{ name: "reviewer", description: "Review changes." }], + agentProviders: [{ + name: "codex", + model: { supported: true, discovery: "model_dependent" }, + effort: { + supported: true, + semantics: "reasoning_effort", + discovery: "model_dependent", + }, + }], + agents: [{ + name: "reviewer", + description: "Review changes.", + provider: "codex", + model: "gpt-5.4", + effort: "high", + }], activeWorkflows: [{ id: "wfr_1", name: "Review", @@ -62,8 +76,22 @@ const parsed = enabledSchema.parse({ }], instruction: "Reuse this workspace.", }); -assert.deepEqual(parsed.agentProviders, ["codex"]); -assert.deepEqual(parsed.agents, [{ name: "reviewer", description: "Review changes." }]); +assert.deepEqual(parsed.agentProviders, [{ + name: "codex", + model: { supported: true, discovery: "model_dependent" }, + effort: { + supported: true, + semantics: "reasoning_effort", + discovery: "model_dependent", + }, +}]); +assert.deepEqual(parsed.agents, [{ + name: "reviewer", + description: "Review changes.", + provider: "codex", + model: "gpt-5.4", + effort: "high", +}]); assert.deepEqual(parsed.activeWorkflows, [{ id: "wfr_1", name: "Review", diff --git a/src/pi-tools.ts b/src/pi-tools.ts index 238b9c54..43d3fc83 100644 --- a/src/pi-tools.ts +++ b/src/pi-tools.ts @@ -28,6 +28,7 @@ export type ToolResponse = { interface ToolContext { cwd: string; root: string; + workspaceId?: string; readRoots?: string[]; } @@ -119,7 +120,18 @@ export async function listDirectoryTool(input: LsToolInput, context: ToolContext } export async function runShellTool(input: BashToolInput, context: ToolContext): Promise { - const tool = createBashTool(context.cwd); + const tool = createBashTool(context.cwd, { + // Keep CLI orchestration launched through MCP attached to the workspace + // that owns this shell call, including non-Git and nested workspaces. + spawnHook: ({ env, ...spawn }) => ({ + ...spawn, + env: { + ...env, + ...(context.workspaceId ? { DEVSPACE_WORKSPACE_ID: context.workspaceId } : {}), + DEVSPACE_WORKSPACE_ROOT: context.root, + }, + }), + }); const timeout = input.timeout === undefined ? 30 : Math.min(input.timeout, 300); return runTool((params) => tool.execute("run_shell", params), { diff --git a/src/server.ts b/src/server.ts index 1463fe49..a9243b6c 100644 --- a/src/server.ts +++ b/src/server.ts @@ -201,7 +201,7 @@ function serverInstructions(config: ServerConfig): string { const agentsMd = `Follow instructions returned by ${toolNames.openWorkspace}. Before working under a path listed in availableAgentsFiles, use ${toolNames.read} to inspect that instruction file and follow it. `; - return `Use DevSpace as a local coding workspace. Call ${toolNames.openWorkspace} once per project folder or worktree to obtain a workspaceId. Reuse that same workspaceId for all later file, search, edit, write, show-changes, and shell tools in that folder; do not call ${toolNames.openWorkspace} again unless switching folders/worktrees, changing checkout/worktree mode, the workspaceId is rejected as unknown, or the user explicitly asks to reopen. ${agentsMd}${skills}${inspection}Prefer ${toolNames.edit} for targeted modifications, ${toolNames.write} only for new files or complete rewrites, and ${toolNames.shell} for tests, builds, git inspection, package scripts, and commands that are better executed by the shell. Do not create or modify files with ${toolNames.shell}; avoid shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or any command whose purpose is to write project files.${showChangesInstruction}`; + return `Use DevSpace as a local coding workspace. Call ${toolNames.openWorkspace} once per project folder or worktree to obtain a workspaceId. Reuse that same workspaceId for all later file, search, edit, write, show-changes, and shell tools in that folder; do not call ${toolNames.openWorkspace} again unless switching folders/worktrees, changing checkout/worktree mode, the workspaceId is rejected as unknown, or the user explicitly asks to reopen. ${agentsMd}${skills}${inspection}Prefer ${toolNames.edit} for targeted modifications, ${toolNames.write} only for new files or complete rewrites, and ${toolNames.shell} for tests, builds, git inspection, package scripts, and commands that are better executed by the shell. Use ${toolNames.shell} for the supported CLI orchestration commands devspace agents ... and devspace workflow ...; those commands may delegate work or write workflow state. For other commands, do not create or modify project files with ${toolNames.shell}; avoid shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or any command whose purpose is to write project files.${showChangesInstruction}`; } function resultOutputSchema(extra: z.ZodRawShape = {}): z.ZodRawShape { @@ -229,6 +229,22 @@ const workspaceAgentsFileOutputSchema = z.object({ const workspaceLocalAgentOutputSchema = z.object({ name: z.string(), description: z.string(), + provider: z.string(), + model: z.string().optional(), + effort: z.string().optional(), +}); + +const workspaceLocalAgentProviderOutputSchema = z.object({ + name: z.string(), + model: z.object({ + supported: z.boolean(), + discovery: z.enum(["provider_static", "model_dependent", "session_dynamic"]), + }), + effort: z.object({ + supported: z.boolean(), + semantics: z.enum(["reasoning_effort", "thinking_level", "model_variant"]), + discovery: z.enum(["provider_static", "model_dependent", "session_dynamic"]), + }), }); export function openWorkspaceOutputSchema(config: ServerConfig): z.ZodRawShape { @@ -252,7 +268,7 @@ export function openWorkspaceOutputSchema(config: ServerConfig): z.ZodRawShape { skills: z.array(workspaceSkillOutputSchema), ...(config.subagents ? { - agentProviders: z.array(z.string()), + agentProviders: z.array(workspaceLocalAgentProviderOutputSchema), agents: z.array(workspaceLocalAgentOutputSchema), } : {}), @@ -280,6 +296,33 @@ const workflowRunSummaryOutputSchema = z.object({ calls: workflowCallCountsOutputSchema, }); +function formatVisibleAgentProvider(provider: { + name: string; + model: { supported: boolean; discovery: string }; + effort: { supported: boolean; semantics: string; discovery: string }; +}): string { + const model = provider.model.supported + ? `model (${provider.model.discovery})` + : "model unsupported"; + const effort = provider.effort.supported + ? `effort (${provider.effort.semantics}, ${provider.effort.discovery})` + : "effort unsupported"; + return `${provider.name} — ${model}; ${effort}`; +} + +function formatVisibleAgent(agent: { + name: string; + description: string; + provider: string; + model?: string; + effort?: string; +}): string { + const target = [agent.provider, agent.model && `model=${agent.model}`, agent.effort && `effort=${agent.effort}`] + .filter(Boolean) + .join(", "); + return `${agent.name} (${target}) — ${agent.description}`; +} + const reviewFileOutputSchema = z.object({ path: z.string(), previousPath: z.string().optional(), @@ -797,11 +840,8 @@ function createMcpServer( const agentCatalog = config.subagents ? buildLocalAgentCatalog(workspace.agentProfiles, localAgentProviders) : undefined; - const visibleAgentProviders = agentCatalog?.providers.map((provider) => provider.name) ?? []; - const visibleAgents = agentCatalog?.profiles.map((agent) => ({ - name: agent.name, - description: agent.description, - })) ?? []; + const visibleAgentProviders = agentCatalog?.providers ?? []; + const visibleAgents = agentCatalog?.profiles ?? []; const loadedAgentsFiles = agentsFiles.map((file) => ({ path: formatAgentsPath(file.path, workspace.root), content: file.content, @@ -842,10 +882,10 @@ function createMcpServer( ? `Available skills: ${visibleSkills.map((skill) => skill.name).join(", ")}` : undefined, visibleAgentProviders.length > 0 - ? `Available subagent providers: ${visibleAgentProviders.join(", ")}` + ? `Available subagent providers: ${visibleAgentProviders.map(formatVisibleAgentProvider).join("; ")}` : undefined, visibleAgents.length > 0 - ? `Available subagent profiles: ${visibleAgents.map((agent) => `${agent.name} — ${agent.description}`).join(", ")}` + ? `Available subagent profiles: ${visibleAgents.map(formatVisibleAgent).join("; ")}` : undefined, instruction, ].filter(Boolean).join("\n"), @@ -1518,8 +1558,8 @@ function createMcpServer( { title: "Bash", description: config.toolMode !== "full" - ? `Run a shell command inside an open workspace. Use only for tests, builds, git inspection, package scripts, search, file discovery, and directory inspection. In minimal tool mode, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} are disabled; use command-line tools such as grep, rg, find, ls, and tree for those read-only inspection actions. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read} for direct file reads. Call open_workspace first and pass workspaceId. This is powerful local execution and should only be exposed behind strong authentication.` - : `Run a shell command inside an open workspace. Use only for tests, builds, git inspection, package scripts, and commands that are better executed by the shell. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection. Call open_workspace first and pass workspaceId. This is powerful local execution and should only be exposed behind strong authentication.`, + ? `Run a shell command inside an open workspace. Use only for tests, builds, git inspection, package scripts, search, file discovery, and directory inspection. In minimal tool mode, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} are disabled; use command-line tools such as grep, rg, find, ls, and tree for those read-only inspection actions. The supported CLI orchestration commands devspace agents ... and devspace workflow ... are also allowed; they may delegate work or write workflow state. Do not use ${toolNames.shell} for other project-file changes. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read} for direct file reads. Call open_workspace first and pass workspaceId. This is powerful local execution and should only be exposed behind strong authentication.` + : `Run a shell command inside an open workspace. Use only for tests, builds, git inspection, package scripts, and commands that are better executed by the shell. The supported CLI orchestration commands devspace agents ... and devspace workflow ... are also allowed; they may delegate work or write workflow state. Do not use ${toolNames.shell} for other project-file changes. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection. Call open_workspace first and pass workspaceId. This is powerful local execution and should only be exposed behind strong authentication.`, inputSchema: { workspaceId: z .string() @@ -1527,7 +1567,7 @@ function createMcpServer( command: z .string() .describe( - `Shell command to run. Must not create or modify project files; use ${toolNames.edit} or ${toolNames.write} for file changes.`, + `Shell command to run. Use ${toolNames.edit} or ${toolNames.write} for direct project-file changes; devspace agents ... and devspace workflow ... are the supported orchestration exceptions.`, ), workingDirectory: z .string() @@ -1556,6 +1596,7 @@ function createMcpServer( const response = await runShellTool(input, { cwd, root: workspace.root, + workspaceId, }); if (response.isError) { diff --git a/src/ui/card-types.ts b/src/ui/card-types.ts index 160bc53d..4c9acb9a 100644 --- a/src/ui/card-types.ts +++ b/src/ui/card-types.ts @@ -57,10 +57,24 @@ export interface ToolResultCard { path?: string; }>; activeWorkflows?: ActiveWorkflowSummary[]; - agentProviders?: string[]; + agentProviders?: Array<{ + name?: string; + model?: { + supported?: boolean; + discovery?: string; + }; + effort?: { + supported?: boolean; + semantics?: string; + discovery?: string; + }; + }>; agents?: Array<{ name?: string; description?: string; + provider?: string; + model?: string; + effort?: string; }>; instruction?: string; } diff --git a/src/ui/workspace-dashboard.ts b/src/ui/workspace-dashboard.ts index 321e96ac..9e274980 100644 --- a/src/ui/workspace-dashboard.ts +++ b/src/ui/workspace-dashboard.ts @@ -81,6 +81,11 @@ export function renderWorkspaceDashboard( card.agents.map((agent) => ({ title: agent.name ?? "Unnamed profile", description: agent.description, + meta: [ + agent.provider, + agent.model && `model=${agent.model}`, + agent.effort && `effort=${agent.effort}`, + ].filter(Boolean).join(", "), })), "No agent profiles loaded.", ), @@ -157,7 +162,19 @@ function renderKeyValues(entries: Array<[string, string]>): HTMLElement { function renderProviderList(card: ToolResultCard): HTMLElement { return renderList( - card.agentProviders?.map((provider) => ({ title: provider })) ?? [], + card.agentProviders?.map((provider) => ({ + title: provider.name ?? "Unknown provider", + meta: [ + provider.model?.supported === false + ? "model unsupported" + : provider.model?.discovery && `model (${provider.model.discovery})`, + provider.effort?.supported === false + ? "effort unsupported" + : provider.effort?.semantics && provider.effort.discovery + ? `effort (${provider.effort.semantics}, ${provider.effort.discovery})` + : undefined, + ].filter(Boolean).join("; "), + })) ?? [], "No subagent providers exposed.", ); }