diff --git a/docs/OVERVIEW.md b/docs/OVERVIEW.md index 9a1bc063..2190c9f4 100644 --- a/docs/OVERVIEW.md +++ b/docs/OVERVIEW.md @@ -135,6 +135,8 @@ The agent runs: reason → call tool(s) → reason again → answer. Tool array | `deepAgents.js` | `createDeepAgentsOrchestrator()` — creates the Deep Agents orchestrator with coding and utility agents; loads per-project agent prompt configuration | The orchestrator routes tasks automatically — the system prompt delegates every task to the orchestrator, which manages routing, state, and observability natively. + +**Tool Classification:** Tools and skills are classified by agent type (`orchestrator`, `subagent`, or `shared`) in `src/tools/index.js`. The orchestrator receives only `orchestrator`-classified and `shared` tools/skills, while the coding subagent receives `subagent`-classified and `shared` tools/skills. This ensures each agent has only the capabilities it needs for its role. The classification is applied via `buildToolConfig()`'s `classificationFilter` parameter and `filterSkillPaths()` helper function. --- diff --git a/docs/TUTORIAL.md b/docs/TUTORIAL.md index 94b4e282..cc99c115 100644 --- a/docs/TUTORIAL.md +++ b/docs/TUTORIAL.md @@ -327,6 +327,79 @@ This mode is used by internal cron jobs and NPM installations. --- +## 🤖 Automating Coding Tasks with the Task Tool + +`madz` can delegate complex, multi-step coding tasks to specialized subagents through the `task` tool. Instead of you orchestrating each step manually — checking git status, running lint, editing files, committing — you describe the outcome you want and a subagent handles the execution. The machine does not wait for idle terminals; it waits for clear instructions. + +### Available Agent Types + +Two agent types are available, each with a distinct scope: + +- **`general-purpose`** — Research, file searches, multi-step tasks that span multiple domains. Has access to all tools. Use this when the task is exploratory, involves gathering information, or doesn't fit neatly into a single specialty. + +- **`coding-agent`** — Specialized for code-related work: file editing, debugging, implementation, code review, and git operations. This agent understands project conventions, follows linting standards, and respects commit message formatting rules. Use this when the task touches source code, tests, or version control. + +### Concrete Examples + +**Example 1: Commit and Push** + +``` +task coding-agent: commit and push to this branch, we have an open pr +``` + +The coding-agent checks git status, stages changed files, crafts a descriptive commit message following the project's conventional commit format, and pushes to the remote branch. It will not push without explicit approval — see the notes below. + +**Example 2: Run Lint, Fix Errors, Commit, and Push** + +``` +task coding-agent: run lint, fix any errors, then commit and push +``` + +The coding-agent runs the project's lint command, identifies each issue, applies fixes to the relevant files, re-runs lint to verify all issues are resolved, and then proceeds to commit and push. If a fix is ambiguous or risky, the agent will pause and ask for guidance rather than guessing. + +**Example 3: Update a PR** + +``` +task coding-agent: update PR #123 with the latest changes from this branch +``` + +The coding-agent can interact with GitHub pull requests — updating PR descriptions, adding review comments, requesting changes, or merging. It reads the current branch state, diffs against the target branch, and composes meaningful descriptions from the actual code changes. + +**Example 4: Create an Issue** + +``` +task coding-agent: create an issue for memory leak in session manager +``` + +The coding-agent can create GitHub issues with proper titles, descriptions, labels, and categorization. It will search the codebase for relevant context, reference related files, and suggest labels based on the project's issue taxonomy. + +**Example 5: Debug a Failing Test** + +``` +task coding-agent: debug the failing test in tests/unit/skills.test.js and fix it +``` + +The coding-agent runs the specified test, analyzes the failure output, traces the root cause through the relevant source files, and applies a fix. It re-runs the test to confirm the fix resolves the issue and does not introduce regressions in related tests. + +### Best Practices + +- **Be specific in your delegation.** Clear, unambiguous instructions produce better results. "Fix the lint errors" is good; "run eslint on src/ and fix all errors without changing the public API" is better. +- **The coding-agent operates in the project's CWD by default.** All file paths and commands are resolved relative to the working directory. +- **It follows project conventions.** The agent reads `AGENTS.md` rules, respects the project's commit message format, and adheres to linting and formatting standards. +- **For git operations, it respects branch protection.** It will not force-push or bypass protected branches. +- **Complex tasks benefit from step-by-step instructions.** If a task has multiple phases, describe them in order and the agent will execute sequentially. + +### Important Notes + +- The coding-agent **never rebases** without explicit agreement. +- It **never pushes** without explicit user approval. +- It **never changes branches** without permission. +- It follows the project's **conventional commit format** for all commits. + +These guardrails exist so you can delegate with confidence. The agent is a collaborator, not an autonomous actor. It acts, but it does not overreach. + +--- + ## 🔧 Troubleshooting ### Docker-Specific diff --git a/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/.openspec.yaml b/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/.openspec.yaml new file mode 100644 index 00000000..43e65ca6 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-03 diff --git a/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/design.md b/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/design.md new file mode 100644 index 00000000..287d41ef --- /dev/null +++ b/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/design.md @@ -0,0 +1,72 @@ +## Context + +The Madz project uses a Deep Agents architecture where an orchestrator agent delegates tasks to subagents. Currently, `src/agent/deepAgents.js` creates both the orchestrator and subagents with identical tool and skill sets via a single `buildToolConfig()` call. The orchestrator receives all 16 tools (terminal, process, todo, sessionSearch, clarify, webSearch, webExtract, visionAnalyze, imageGenerate, executeCode, cronJob, textToSpeech, mixtureOfAgents, sampling, date, scanAgents) plus all discovered skill paths. This means the orchestrator has access to tools it should never need (like terminal execution or code editing), and subagents don't have differentiated capabilities. + +The project uses OpenSpec for feature development. Tools are defined in `src/tools/index.js` with a `TOOL_PERMISSIONS` map and `TOOL_FACTORIES` object. Skills are discovered via `SkillRegistry` in `src/skills/registry.js`. + +## Goals / Non-Goals + +**Goals:** +- Define a classification schema for tools (orchestrator, subagent, shared) +- Modify `buildToolConfig()` to support filtering by classification +- Update `deepAgents.js` to assign tools based on agent type +- Filter skill paths by agent type (orchestrator gets coordination skills only, subagents get execution skills) +- Maintain backward compatibility — `buildToolConfig()` without a filter includes all tools + +**Non-Goals:** +- Adding new tools or skills +- Changing the tool permission system +- Modifying the SkillRegistry or skill discovery mechanism +- Creating new subagent types beyond the existing codingSubAgent +- Runtime reconfiguration of tool assignments + +## Decisions + +### Decision 1: Classification Map in `src/tools/index.js` +**Choice:** Add a `TOOL_CLASSIFICATIONS` map alongside the existing `TOOL_PERMISSIONS` map. +**Rationale:** Keeps classification co-located with tool definitions. Each tool maps to a classification string. This is simpler than adding a classification field to each factory function. +**Alternatives considered:** +- Adding classification to TOOL_PERMISSIONS values — rejected because it conflates permissions with classification +- Separate classification file — rejected because it creates a second source of truth + +### Decision 2: Filter Parameter in `buildToolConfig()` +**Choice:** Add an optional `classificationFilter` parameter (array of strings like `['orchestrator', 'shared']`) to `buildToolConfig()`. +**Rationale:** Optional parameter maintains backward compatibility. When not provided, all tools are included (current behavior). +**Alternatives considered:** +- Boolean `forSubagent` flag — rejected because it doesn't support a third "shared" category +- Separate `buildOrchestratorTools()` and `buildSubagentTools()` functions — rejected because it duplicates the factory logic + +### Decision 3: Skill Classification +**Choice:** Classify skills into orchestrator-only, subagent-only, and shared. Initially, no skills are classified for the orchestrator (it has no specialized skills), while all skills are available to subagents. +**Rationale:** The orchestrator's role is coordination — delegation, routing, synthesis. Skills are execution-focused. This can be refined later as new skills are added. +**Alternatives considered:** +- Give orchestrator all skills — rejected because it defeats the purpose of classification +- Hardcode skill assignments per agent — rejected because it duplicates configuration + +### Decision 4: Two-Call Pattern in `deepAgents.js` +**Choice:** Call `buildToolConfig()` twice with different filters — once for the orchestrator, once for the codingSubAgent. +**Rationale:** Clear, explicit, and easy to understand. Each agent gets its own tool set. +**Alternatives considered:** +- Build once and clone with filters — rejected because it adds complexity without benefit +- Build a master tool set and filter at agent creation time — rejected because it's less explicit + +## Risks / Trade-offs + +[Risk] Orchestrator may need a tool that was classified as subagent-only → [Mitigation] Classification can be updated; add a "shared" category for tools both agents need +[Risk] Performance impact from calling `buildToolConfig()` twice → [Mitigation] Negligible — tool creation is lightweight +[Risk] Future subagent types may need different tool sets → [Mitigation] The classification system supports this; add new subagent types with appropriate filters +[Risk] Breaking changes if classification is wrong → [Mitigation] Add tests to verify each agent has expected tools + +## Migration Plan + +1. Add `TOOL_CLASSIFICATIONS` map to `src/tools/index.js` +2. Modify `buildToolConfig()` to accept `classificationFilter` parameter +3. Update `deepAgents.js` to use two calls with appropriate filters +4. Update `docs/OVERVIEW.md` to document the new architecture +5. No rollback needed — change is additive and backward compatible + +## Open Questions + +- Should the orchestrator have any skills at all, or should all skills be subagent-only? +- Are there any tools that should be "shared" (available to both agents)? +- How should classification be documented for future tool additions? \ No newline at end of file diff --git a/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/proposal.md b/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/proposal.md new file mode 100644 index 00000000..61f42967 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/proposal.md @@ -0,0 +1,27 @@ +## Why + +The orchestrator and subagents in `src/agent/deepAgents.js` currently receive identical, full sets of tools and skills. This is architecturally unsound — the orchestrator should only have coordination tools (delegation, routing, synthesis) while subagents should have execution tools (code editing, terminal access, file operations). Giving everything to the orchestrator is inefficient and makes it harder to reason about agent capabilities. + +## What Changes + +- Add a `TOOL_CLASSIFICATIONS` map in `src/tools/index.js` that classifies each tool as `orchestrator`, `subagent`, or `shared` +- Modify `buildToolConfig()` in `src/tools/index.js` to accept an optional `classificationFilter` parameter that filters tools by classification +- Update `createDeepAgentsOrchestrator()` in `src/agent/deepAgents.js` to call `buildToolConfig()` twice — once for the orchestrator and once for the coding subagent — each with the appropriate filter +- Filter skill paths so the orchestrator receives only orchestrator-classified and shared skills (initially: no skills for orchestrator, all skills for subagents) +- Add inline documentation explaining the classification system and agent-specific tool assignments + +## Capabilities + +### New Capabilities +- `tool-classification`: Classification schema for tools (orchestrator, subagent, shared) with filtering support in `buildToolConfig()` +- `agent-tool-assignment`: Agent-specific tool assignment in `deepAgents.js` based on classification filters + +### Modified Capabilities + + +## Impact + +- `src/tools/index.js` — New `TOOL_CLASSIFICATIONS` map, modified `buildToolConfig()` signature +- `src/agent/deepAgents.js` — Modified tool assignment logic, skill path filtering +- `docs/OVERVIEW.md` — Architecture documentation update +- Backward compatible: `buildToolConfig()` without `classificationFilter` includes all tools \ No newline at end of file diff --git a/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/specs/agent-tool-assignment/spec.md b/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/specs/agent-tool-assignment/spec.md new file mode 100644 index 00000000..1320ea5c --- /dev/null +++ b/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/specs/agent-tool-assignment/spec.md @@ -0,0 +1,33 @@ +## ADDED Requirements + +### Requirement: Orchestrator SHALL have minimal tool set for coordination +The orchestrator agent SHALL only receive tools necessary for its coordination role: delegation, routing, and synthesis. It SHALL NOT receive execution tools (terminal, code editing, file operations). + +#### Scenario: Orchestrator lacks execution tools +- **WHEN** the orchestrator is created +- **THEN** tools like `terminal`, `process`, `executeCode`, and `todo` are NOT available to the orchestrator + +#### Scenario: Orchestrator has coordination tools +- **WHEN** the orchestrator is created +- **THEN** tools like `clarify`, `sessionSearch`, and `scanAgents` are available to the orchestrator + +### Requirement: Subagents SHALL have execution tools for task completion +Subagent agents SHALL receive tools necessary for their execution role: file operations, code editing, terminal access, and task management. + +#### Scenario: Coding subagent has execution tools +- **WHEN** the coding subagent is created +- **THEN** tools like `terminal`, `process`, `todo`, `executeCode`, and `web` tools are available + +### Requirement: Shared tools SHALL be available to both agents +Tools classified as `shared` SHALL be available to both the orchestrator and subagents. + +#### Scenario: Shared tool availability +- **WHEN** a tool is classified as `shared` +- **THEN** it is available to both the orchestrator and all subagents + +### Requirement: Tool assignment SHALL be configurable per agent type +The tool assignment system SHALL support adding new agent types with custom tool sets without modifying the core classification logic. + +#### Scenario: Adding a new agent type +- **WHEN** a new subagent type is added to `deepAgents.js` +- **THEN** it can receive a custom tool set by specifying the appropriate classification filter \ No newline at end of file diff --git a/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/specs/tool-classification/spec.md b/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/specs/tool-classification/spec.md new file mode 100644 index 00000000..36e012be --- /dev/null +++ b/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/specs/tool-classification/spec.md @@ -0,0 +1,59 @@ +## ADDED Requirements + +### Requirement: Tools SHALL be classified by agent type +Each tool in `src/tools/index.js` SHALL have a classification indicating which agent type(s) may use it. Classifications are: `orchestrator`, `subagent`, or `shared`. + +#### Scenario: Classification map exists +- **WHEN** `src/tools/index.js` is loaded +- **THEN** a `TOOL_CLASSIFICATIONS` map exists mapping each tool name to one of: `orchestrator`, `subagent`, or `shared` + +#### Scenario: All tools have a classification +- **WHEN** all tools are defined in `TOOL_FACTORIES` +- **THEN** every tool name has a corresponding entry in `TOOL_CLASSIFICATIONS` + +### Requirement: `buildToolConfig()` SHALL support classification filtering +The `buildToolConfig()` function SHALL accept an optional `classificationFilter` parameter that filters tools by their classification. + +#### Scenario: No filter returns all tools +- **WHEN** `buildToolConfig()` is called without `classificationFilter` +- **THEN** all tools that pass permission checks are included (current behavior) + +#### Scenario: Filter with orchestrator classification +- **WHEN** `buildToolConfig()` is called with `classificationFilter: ['orchestrator', 'shared']` +- **THEN** only tools classified as `orchestrator` or `shared` are included + +#### Scenario: Filter with subagent classification +- **WHEN** `buildToolConfig()` is called with `classificationFilter: ['subagent', 'shared']` +- **THEN** only tools classified as `subagent` or `shared` are included + +### Requirement: Orchestrator SHALL receive only orchestrator-classified and shared tools +The orchestrator agent in `deepAgents.js` SHALL receive a filtered tool set containing only tools classified as `orchestrator` or `shared`. + +#### Scenario: Orchestrator tool assignment +- **WHEN** `createDeepAgentsOrchestrator()` creates the orchestrator +- **THEN** the orchestrator receives tools filtered by `['orchestrator', 'shared']` + +### Requirement: Subagents SHALL receive subagent-classified and shared tools +Subagent agents in `deepAgents.js` SHALL receive a filtered tool set containing only tools classified as `subagent` or `shared`. + +#### Scenario: Coding subagent tool assignment +- **WHEN** `createDeepAgentsOrchestrator()` creates the coding subagent +- **THEN** the coding subagent receives tools filtered by `['subagent', 'shared']` + +### Requirement: Skills SHALL be classified by agent type +Skills SHALL be classified into categories: `orchestrator`, `subagent`, or `shared`. The orchestrator SHALL receive only orchestrator-classified and shared skills. Subagents SHALL receive all skills (subagent and shared). + +#### Scenario: Skill path filtering for orchestrator +- **WHEN** `createDeepAgentsOrchestrator()` assigns skills to the orchestrator +- **THEN** only skills classified as `orchestrator` or `shared` are included + +#### Scenario: Skill path filtering for subagent +- **WHEN** `createDeepAgentsOrchestrator()` assigns skills to the coding subagent +- **THEN** all skills are included (subagent and shared) + +### Requirement: Classification SHALL be documented for new tools +When a new tool is added to `src/tools/index.js`, its classification SHALL be documented in the `TOOL_CLASSIFICATIONS` map and in code comments. + +#### Scenario: New tool requires classification +- **WHEN** a new tool is added to `TOOL_FACTORIES` +- **THEN** a corresponding entry MUST be added to `TOOL_CLASSIFICATIONS` with a clear rationale \ No newline at end of file diff --git a/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/tasks.md b/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/tasks.md new file mode 100644 index 00000000..f16cff63 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-classify-assign-tools-skills-orchestrator-subagents/tasks.md @@ -0,0 +1,41 @@ +## 1. Add Tool Classification Map + +- [x] 1.1 Define `TOOL_CLASSIFICATIONS` map in `src/tools/index.js` with all 16 tools classified as orchestrator, subagent, or shared +- [x] 1.2 Add code comments explaining the classification rationale for each tool +- [x] 1.3 Verify all tools in `TOOL_FACTORIES` have a corresponding entry in `TOOL_CLASSIFICATIONS` + +## 2. Modify `buildToolConfig()` to Support Classification Filtering + +- [x] 2.1 Add optional `classificationFilter` parameter to `buildToolConfig()` function signature +- [x] 2.2 Implement filtering logic that excludes tools not matching the classification filter +- [x] 2.3 Ensure backward compatibility — when `classificationFilter` is not provided, all tools are included +- [x] 2.4 Add JSDoc documentation for the new parameter + +## 3. Update `deepAgents.js` for Agent-Specific Tool Assignment + +- [x] 3.1 Modify `createDeepAgentsOrchestrator()` to call `buildToolConfig()` twice — once for orchestrator with `['orchestrator', 'shared']` filter, once for codingSubAgent with `['subagent', 'shared']` filter +- [x] 3.2 Update codingSubAgent to receive the filtered subagent tool set instead of the full `tools` object +- [x] 3.3 Update orchestrator to receive the filtered orchestrator tool set instead of the full `tools` object +- [x] 3.4 Remove the shared `const tools` variable and replace with agent-specific tool variables + +## 4. Filter Skill Paths by Agent Type + +- [x] 4.1 Create a skill classification mechanism — either a classification map or a filter function +- [x] 4.2 Assign skill paths to the codingSubAgent (all skills) +- [x] 4.3 Assign filtered skill paths to the orchestrator (orchestrator-classified and shared skills only) +- [x] 4.4 Update the `createDeepAgent()` call to use agent-specific skill paths + +## 5. Update Documentation + +- [x] 5.1 Update `docs/OVERVIEW.md` to document the new tool classification architecture +- [x] 5.2 Add inline comments in `deepAgents.js` explaining the classification-based assignment +- [x] 5.3 Document the classification system in `src/tools/index.js` + +## 6. Verify and Test + +- [x] 6.1 Verify orchestrator does NOT have subagent-only tools (terminal, process, executeCode, todo, etc.) +- [x] 6.2 Verify codingSubAgent has all subagent tools needed for execution +- [x] 6.3 Verify both agents have shared tools (clarify, date, etc.) +- [x] 6.4 Run `npm run lint` to ensure no lint errors +- [x] 6.5 Run `npm run test` to ensure all tests pass +- [x] 6.6 Run `npm start` to verify application starts without crashing \ No newline at end of file diff --git a/openspec/specs/agent-tool-assignment/spec.md b/openspec/specs/agent-tool-assignment/spec.md new file mode 100644 index 00000000..aeae2359 --- /dev/null +++ b/openspec/specs/agent-tool-assignment/spec.md @@ -0,0 +1,37 @@ +# agent-tool-assignment Specification + +## Purpose +TBD - created by archiving change classify-assign-tools-skills-orchestrator-subagents. Update Purpose after archive. +## Requirements +### Requirement: Orchestrator SHALL have minimal tool set for coordination +The orchestrator agent SHALL only receive tools necessary for its coordination role: delegation, routing, and synthesis. It SHALL NOT receive execution tools (terminal, code editing, file operations). + +#### Scenario: Orchestrator lacks execution tools +- **WHEN** the orchestrator is created +- **THEN** tools like `terminal`, `process`, `executeCode`, and `todo` are NOT available to the orchestrator + +#### Scenario: Orchestrator has coordination tools +- **WHEN** the orchestrator is created +- **THEN** tools like `clarify`, `sessionSearch`, and `scanAgents` are available to the orchestrator + +### Requirement: Subagents SHALL have execution tools for task completion +Subagent agents SHALL receive tools necessary for their execution role: file operations, code editing, terminal access, and task management. + +#### Scenario: Coding subagent has execution tools +- **WHEN** the coding subagent is created +- **THEN** tools like `terminal`, `process`, `todo`, `executeCode`, and `web` tools are available + +### Requirement: Shared tools SHALL be available to both agents +Tools classified as `shared` SHALL be available to both the orchestrator and subagents. + +#### Scenario: Shared tool availability +- **WHEN** a tool is classified as `shared` +- **THEN** it is available to both the orchestrator and all subagents + +### Requirement: Tool assignment SHALL be configurable per agent type +The tool assignment system SHALL support adding new agent types with custom tool sets without modifying the core classification logic. + +#### Scenario: Adding a new agent type +- **WHEN** a new subagent type is added to `deepAgents.js` +- **THEN** it can receive a custom tool set by specifying the appropriate classification filter + diff --git a/openspec/specs/tool-classification/spec.md b/openspec/specs/tool-classification/spec.md new file mode 100644 index 00000000..6a298e8f --- /dev/null +++ b/openspec/specs/tool-classification/spec.md @@ -0,0 +1,63 @@ +# tool-classification Specification + +## Purpose +TBD - created by archiving change classify-assign-tools-skills-orchestrator-subagents. Update Purpose after archive. +## Requirements +### Requirement: Tools SHALL be classified by agent type +Each tool in `src/tools/index.js` SHALL have a classification indicating which agent type(s) may use it. Classifications are: `orchestrator`, `subagent`, or `shared`. + +#### Scenario: Classification map exists +- **WHEN** `src/tools/index.js` is loaded +- **THEN** a `TOOL_CLASSIFICATIONS` map exists mapping each tool name to one of: `orchestrator`, `subagent`, or `shared` + +#### Scenario: All tools have a classification +- **WHEN** all tools are defined in `TOOL_FACTORIES` +- **THEN** every tool name has a corresponding entry in `TOOL_CLASSIFICATIONS` + +### Requirement: `buildToolConfig()` SHALL support classification filtering +The `buildToolConfig()` function SHALL accept an optional `classificationFilter` parameter that filters tools by their classification. + +#### Scenario: No filter returns all tools +- **WHEN** `buildToolConfig()` is called without `classificationFilter` +- **THEN** all tools that pass permission checks are included (current behavior) + +#### Scenario: Filter with orchestrator classification +- **WHEN** `buildToolConfig()` is called with `classificationFilter: ['orchestrator', 'shared']` +- **THEN** only tools classified as `orchestrator` or `shared` are included + +#### Scenario: Filter with subagent classification +- **WHEN** `buildToolConfig()` is called with `classificationFilter: ['subagent', 'shared']` +- **THEN** only tools classified as `subagent` or `shared` are included + +### Requirement: Orchestrator SHALL receive only orchestrator-classified and shared tools +The orchestrator agent in `deepAgents.js` SHALL receive a filtered tool set containing only tools classified as `orchestrator` or `shared`. + +#### Scenario: Orchestrator tool assignment +- **WHEN** `createDeepAgentsOrchestrator()` creates the orchestrator +- **THEN** the orchestrator receives tools filtered by `['orchestrator', 'shared']` + +### Requirement: Subagents SHALL receive subagent-classified and shared tools +Subagent agents in `deepAgents.js` SHALL receive a filtered tool set containing only tools classified as `subagent` or `shared`. + +#### Scenario: Coding subagent tool assignment +- **WHEN** `createDeepAgentsOrchestrator()` creates the coding subagent +- **THEN** the coding subagent receives tools filtered by `['subagent', 'shared']` + +### Requirement: Skills SHALL be classified by agent type +Skills SHALL be classified into categories: `orchestrator`, `subagent`, or `shared`. The orchestrator SHALL receive only orchestrator-classified and shared skills. Subagents SHALL receive all skills (subagent and shared). + +#### Scenario: Skill path filtering for orchestrator +- **WHEN** `createDeepAgentsOrchestrator()` assigns skills to the orchestrator +- **THEN** only skills classified as `orchestrator` or `shared` are included + +#### Scenario: Skill path filtering for subagent +- **WHEN** `createDeepAgentsOrchestrator()` assigns skills to the coding subagent +- **THEN** all skills are included (subagent and shared) + +### Requirement: Classification SHALL be documented for new tools +When a new tool is added to `src/tools/index.js`, its classification SHALL be documented in the `TOOL_CLASSIFICATIONS` map and in code comments. + +#### Scenario: New tool requires classification +- **WHEN** a new tool is added to `TOOL_FACTORIES` +- **THEN** a corresponding entry MUST be added to `TOOL_CLASSIFICATIONS` with a clear rationale + diff --git a/src/agent/deepAgents.js b/src/agent/deepAgents.js index abcc80a9..35dccbcd 100644 --- a/src/agent/deepAgents.js +++ b/src/agent/deepAgents.js @@ -1,13 +1,4 @@ -import { - createDeepAgent, - CompositeBackend, - createSubAgent, - createFilesystemMiddleware, - createMemoryMiddleware, - createSummarizationMiddleware, - createPatchToolCallsMiddleware, -} from "deepagents"; -import { todoListMiddleware } from "langchain"; +import { createDeepAgent, CompositeBackend } from "deepagents"; import { readFileSync } from "node:fs"; import { join } from "node:path"; import { InMemoryStore } from "@langchain/langgraph-checkpoint"; @@ -18,7 +9,32 @@ import { createChatModel } from "../provider/openai.js"; import { buildToolConfig } from "../tools/index.js"; import { createCoreBackend } from "./coreBackend.js"; import { createContextBackend } from "./contextBackend.js"; -import { createSubAgentsBackend } from "./subAgentsBackend.js"; +// Skill classification map — classifies each skill by agent type. +// Skills are discovered dynamically; this map provides the classification +// for filtering. Initially, all skills are classified as "subagent" since +// the orchestrator's role is coordination, not execution. +const SKILL_CLASSIFICATIONS = { + // All skills are subagent-only by default. + // Add entries here to classify skills as "orchestrator" or "shared". +}; + +/** + * Filter skill paths by classification. + * @param {string[]} skillPaths - All discovered skill paths + * @param {string[]} classificationFilter - Classes to include (e.g., ['orchestrator', 'shared']) + * @returns {string[]} Filtered skill paths + */ +function filterSkillPaths(skillPaths, classificationFilter) { + if (!classificationFilter || classificationFilter.length === 0) { + return skillPaths; + } + const filterSet = new Set(classificationFilter); + return skillPaths.filter((path) => { + const skillName = path.split("/").pop()?.replace(".md", "") || path; + const classification = SKILL_CLASSIFICATIONS[skillName] || "subagent"; + return filterSet.has(classification); + }); +} function loadCodeAgentPrompt(baseDir) { try { @@ -51,8 +67,8 @@ export async function createDeepAgentsOrchestrator(checkpointer = null) { const providerConfig = config.providers[providerName] || {}; const model = createChatModel(providerConfig); - // Build tools from config - const tools = await buildToolConfig({ + // Build tools from config — separate sets for orchestrator and subagent + const buildOptions = { permissions: config.sandbox.permissions || [], allowedPaths: config.sandbox.paths, maxReadSize: config.sandbox.maxReadSize || "1mb", @@ -65,41 +81,49 @@ export async function createDeepAgentsOrchestrator(checkpointer = null) { ephemeralTtlDays: config.memory?.ephemeral?.ttlDays || 7, ephemeralMaxEntries: config.memory?.ephemeral?.maxEntries || 10, config, + }; + + // Orchestrator receives coordination tools only (orchestrator + shared classifications) + const orchestratorTools = await buildToolConfig({ + ...buildOptions, + classificationFilter: ["orchestrator", "shared"], + }); + + // Coding subagent receives execution tools (subagent + shared classifications) + const subagentTools = await buildToolConfig({ + ...buildOptions, + classificationFilter: ["subagent", "shared"], }); const coreBackend = createCoreBackend(); const contextBackend = createContextBackend(); - const subAgentsBackend = createSubAgentsBackend(); const contextRoute = "/" + config.memory.contextDir.replace(/^\.?\//, ""); - const codingSubAgent = createSubAgent({ - name: "coding-agent", - description: - "Specialized agent for code-related tasks including file editing, debugging, implementation, and code review.", - systemPrompt: codeAgentPrompt || "You are a coding specialist. Handle all code-related tasks.", - model, - tools, - middleware: [ - todoListMiddleware(), - createFilesystemMiddleware({ backend: coreBackend }), - createSummarizationMiddleware({ backend: subAgentsBackend }), - createPatchToolCallsMiddleware(), - createMemoryMiddleware({ backend: subAgentsBackend }), - ], - }); + // Filter skill paths by agent type + const orchestratorSkills = filterSkillPaths(skillPaths, ["orchestrator", "shared"]); + // Note: subagents receive all skills (skillPaths) — add to createSubAgent when API supports it return createDeepAgent({ model, - tools, + tools: orchestratorTools, systemPrompt, store: new InMemoryStore(), backend: new CompositeBackend(coreBackend, { [contextRoute]: contextBackend, }), - subagents: [codingSubAgent], + subagents: [ + { + name: "coding-agent", + description: + "Specialized agent for code-related tasks including file editing, debugging, implementation, and code review.", + systemPrompt: codeAgentPrompt || "You are a coding specialist. Handle all code-related tasks.", + model, + tools: subagentTools + }, + ], ...(agentsPath && { memory: [agentsPath] }), - ...(skillPaths.length > 0 && { skills: skillPaths }), + ...(orchestratorSkills.length > 0 && { skills: orchestratorSkills }), ...(checkpointer && { checkpointer }), }); } diff --git a/src/tools/index.js b/src/tools/index.js index 3c84da20..f4e69f5a 100644 --- a/src/tools/index.js +++ b/src/tools/index.js @@ -57,6 +57,31 @@ const TOOL_FACTORIES = { scanAgents: createScanAgentsTool, }; +/** + * Maps tool names to their agent type classification. + * - `orchestrator`: Tools the orchestrator uses for coordination (delegation, routing, synthesis) + * - `subagent`: Tools subagents use for execution (code editing, terminal, file operations) + * - `shared`: Tools both orchestrator and subagents may need + */ +export const TOOL_CLASSIFICATIONS = { + terminal: "subagent", // Execution: terminal access for subagents + process: "subagent", // Execution: process management for subagents + todo: "subagent", // Execution: task management for subagents + sessionSearch: "orchestrator", // Coordination: orchestrator searches past sessions for context + clarify: "shared", // Both: may need to clarify with user + webSearch: "shared", // Both: may need to search the web + webExtract: "shared", // Both: may need to extract web content + visionAnalyze: "shared", // Both: may need to analyze images + imageGenerate: "subagent", // Execution: image generation for subagents + executeCode: "subagent", // Execution: code execution for subagents + cronJob: "subagent", // Execution: scheduling for subagents + textToSpeech: "subagent", // Execution: TTS for subagents + mixtureOfAgents: "orchestrator", // Coordination: MOA for orchestrator decision-making + sampling: "orchestrator", // Coordination: memory sampling for orchestrator + date: "shared", // Both: may need date/time info + scanAgents: "orchestrator", // Coordination: scanning for AGENTS.md files +}; + /** * Build an array of LangChain tools gated by sandbox permissions and API keys. * Each tool is created with runtime options captured in a closure, ensuring @@ -73,6 +98,7 @@ const TOOL_FACTORIES = { * @param {string} [options.contextDir] - Directory for memory entries * @param {number} [options.ephemeralTtlDays] - TTL for ephemeral memories * @param {number} [options.ephemeralMaxEntries] - Max concurrent ephemeral entries + * @param {string[]} [options.classificationFilter] - Filter tools by classification (e.g., ['orchestrator', 'shared']). When omitted, all tools are included. * @param {object} [options.config] - Resolved config object from loadConfig() * @param {object} [options.config.providers] - Provider configs (openai, openrouter, fal) * @param {object} [options.config.search] - Search backend configs @@ -91,6 +117,7 @@ export async function buildToolConfig(options) { contextDir = "memory/context/", ephemeralTtlDays = 7, ephemeralMaxEntries = 10, + classificationFilter, config, } = options; @@ -208,5 +235,18 @@ export async function buildToolConfig(options) { } } + // Filter by classification if a filter is provided + if (classificationFilter && classificationFilter.length > 0) { + const filterSet = new Set(classificationFilter); + const filteredTools = []; + for (const tool of tools) { + const classification = TOOL_CLASSIFICATIONS[tool.name]; + if (classification && filterSet.has(classification)) { + filteredTools.push(tool); + } + } + return filteredTools; + } + return tools; } diff --git a/src/tui/app.js b/src/tui/app.js index e9ff6737..b030f99a 100644 --- a/src/tui/app.js +++ b/src/tui/app.js @@ -330,7 +330,12 @@ export default function App({ if (shouldAbort()) return; - finalizeStreaming(responseContent, committedReasoning, lastToolCallDisplay, todoStatusLines); + finalizeStreaming( + responseContent, + committedReasoning, + lastToolCallDisplay, + todoStatusLines, + ); // Persist assistant response to session state if (sessionState) { @@ -798,7 +803,12 @@ export default function App({ * @param {string} lastToolCallDisplay - Tool call display text * @param {string} todoStatusLines - Todo status lines */ - const finalizeStreaming = (responseContent, committedReasoning, lastToolCallDisplay, todoStatusLines) => { + const finalizeStreaming = ( + responseContent, + committedReasoning, + lastToolCallDisplay, + todoStatusLines, + ) => { setMessages((prev) => { const cloned = [...prev]; const last = cloned[cloned.length - 1];