diff --git a/README.md b/README.md index af72553b..c1ffb9a4 100644 --- a/README.md +++ b/README.md @@ -506,7 +506,7 @@ All built-in tools are defined in `src/tools/` and registered as LangChain tools | ---- | ----------- | | `clarify` | Send clarification questions to the user with optional numbered choices. Zero permissions — always registered. | | `compactContext` | Reduce conversation context when LLM context length is exceeded. Tiered retention: retain recent, summarize older, drop oldest. | -| `cronJob` | Manage scheduled cron jobs — create, list, update, pause, resume, run, remove. Persisted to `memory/schedules/`. | +| `cronJob` | Manage scheduled cron jobs — create, list, update, pause, resume, run, remove. Persisted to `memory/schedules/`. Available to the orchestrator agent. | | `createSkill` | Create a spec-compliant skill directory with SKILL.md YAML frontmatter. Optionally scaffolds a `scripts/` directory. | | `date` | Return current date/time in ISO 8601 UTC or human-readable format. Zero permissions — always registered. | | `imageGenerate` | Generate images via FAL.ai flux/klein API. | diff --git a/openspec/changes/archive/2026-08-25-add-orchestrator-cronjob-access/.openspec.yaml b/openspec/changes/archive/2026-08-25-add-orchestrator-cronjob-access/.openspec.yaml new file mode 100644 index 00000000..e685d45e --- /dev/null +++ b/openspec/changes/archive/2026-08-25-add-orchestrator-cronjob-access/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-08-25 diff --git a/openspec/changes/archive/2026-08-25-add-orchestrator-cronjob-access/proposal.md b/openspec/changes/archive/2026-08-25-add-orchestrator-cronjob-access/proposal.md new file mode 100644 index 00000000..2fa6ded1 --- /dev/null +++ b/openspec/changes/archive/2026-08-25-add-orchestrator-cronjob-access/proposal.md @@ -0,0 +1,29 @@ +## Why + +The orchestrator agent needs access to the `cronJob` tool to manage scheduled jobs during active sessions. Currently, `cronJob` is classified only for `security-audit` and `performance` agent types, and it is not included in the `ORCHESTRATOR_TOOLS` array. This means the orchestrator cannot create, list, pause, resume, or remove cron jobs — a gap that prevents self-service scheduling during interactive sessions. + +## What Changes + +- Add `"cronJob"` to the `ORCHESTRATOR_TOOLS` array in `src/tools/index.js` +- Add `"orchestrator"` to the `cronJob` classification in `TOOL_CLASSIFICATIONS` in `src/tools/index.js` +- Update the README.md Built-in Tools table to document that the orchestrator has access to `cronJob` +- Add unit tests verifying the orchestrator has access to `cronJob` + +## Capabilities + +### Modified Capabilities + +- `orchestrator-tools`: Extended to include `cronJob` tool access +- `tool-classifications`: Extended `cronJob` to include `orchestrator` agent type + +## Impact + +- **Affected code**: `src/tools/index.js`, `README.md`, `tests/unit/` (new test file) +- **Dependencies**: None — `cronJob` tool already exists and is fully implemented +- **Tests**: New test file `tests/unit/tools_orchestrator.test.js` + +## Non-goals + +- Adding cronJob access to other agent types beyond orchestrator +- Modifying cronJob tool implementation or permissions +- Adding new cronJob actions or capabilities diff --git a/openspec/changes/archive/2026-08-25-add-orchestrator-cronjob-access/tasks.md b/openspec/changes/archive/2026-08-25-add-orchestrator-cronjob-access/tasks.md new file mode 100644 index 00000000..0d3ec17b --- /dev/null +++ b/openspec/changes/archive/2026-08-25-add-orchestrator-cronjob-access/tasks.md @@ -0,0 +1,27 @@ +## 1. Update ORCHESTRATOR_TOOLS array + +- [x] 1.1 Add "cronJob" to ORCHESTRATOR_TOOLS array in src/tools/index.js (line ~161) +- [x] 1.2 Verify the array remains alphabetically ordered or follows existing convention + +## 2. Update TOOL_CLASSIFICATIONS + +- [x] 2.1 Add "orchestrator" to the cronJob classification in TOOL_CLASSIFICATIONS (line 81) +- [x] 2.2 Verify the classification array follows existing convention (alphabetical order) + +## 3. Update README.md + +- [x] 3.1 Update the Built-in Tools table in README.md to show orchestrator has access to cronJob +- [x] 3.2 Verify the table formatting is consistent with existing entries + +## 4. Write unit tests + +- [x] 4.1 Create tests/unit/tools_orchestrator.test.js +- [x] 4.2 Test that "cronJob" is included in ORCHESTRATOR_TOOLS array +- [x] 4.3 Test that "orchestrator" is included in cronJob's TOOL_CLASSIFICATIONS entry +- [x] 4.4 Test that getToolsForAgentTypes returns cronJob when orchestrator type is queried + +## 5. Verify + +- [x] 5.1 Run npm run test and verify all tests pass +- [x] 5.2 Run npm run lint and fix any lint errors +- [x] 5.3 Run npm run coverage and verify coverage is maintained diff --git a/src/tools/index.js b/src/tools/index.js index f2712943..1a878551 100644 --- a/src/tools/index.js +++ b/src/tools/index.js @@ -78,7 +78,7 @@ export const TOOL_CLASSIFICATIONS = { "coding", ], compactContext: ["debug", "code-review", "research", "coding"], - cronJob: ["security-audit", "performance"], + cronJob: ["orchestrator", "security-audit", "performance"], createSkill: ["documentation"], date: [ "search", @@ -148,6 +148,7 @@ export function getToolsForAgentTypes(agentTypes, tools) { export const ORCHESTRATOR_TOOLS = [ "clarify", "compactContext", + "cronJob", "date", "memory", "process", diff --git a/tests/unit/tools_orchestrator.test.js b/tests/unit/tools_orchestrator.test.js new file mode 100644 index 00000000..e94d4dae --- /dev/null +++ b/tests/unit/tools_orchestrator.test.js @@ -0,0 +1,86 @@ +import { describe, it } from "node:test"; +import { deepStrictEqual, ok } from "node:assert"; + +import { + ORCHESTRATOR_TOOLS, + TOOL_CLASSIFICATIONS, + getToolsForAgentTypes, +} from "../../src/tools/index.js"; + +// Import TOOLS dynamically since it depends on runtime config +let TOOLS; +async function loadTools() { + if (!TOOLS) { + const mod = await import("../../src/tools/index.js"); + TOOLS = mod.TOOLS; + } + return TOOLS; +} + +describe("orchestrator - cronJob access", () => { + it("ORCHESTRATOR_TOOLS should include cronJob", () => { + ok(ORCHESTRATOR_TOOLS.includes("cronJob"), "ORCHESTRATOR_TOOLS should include cronJob"); + }); + + it("ORCHESTRATOR_TOOLS should be an array of strings", () => { + deepStrictEqual( + typeof ORCHESTRATOR_TOOLS, + "object", + "ORCHESTRATOR_TOOLS should be an object (array)", + ); + deepStrictEqual( + Array.isArray(ORCHESTRATOR_TOOLS), + true, + "ORCHESTRATOR_TOOLS should be an array", + ); + ORCHESTRATOR_TOOLS.forEach((tool) => { + deepStrictEqual(typeof tool, "string", `Each tool should be a string, got ${typeof tool}`); + }); + }); + + it("cronJob should be classified for orchestrator", () => { + ok(TOOL_CLASSIFICATIONS.cronJob, "cronJob should have a TOOL_CLASSIFICATIONS entry"); + ok( + TOOL_CLASSIFICATIONS.cronJob.includes("orchestrator"), + "cronJob should include orchestrator in its classifications", + ); + }); + + it("cronJob should retain its existing classifications", () => { + ok( + TOOL_CLASSIFICATIONS.cronJob.includes("security-audit"), + "cronJob should still include security-audit", + ); + ok( + TOOL_CLASSIFICATIONS.cronJob.includes("performance"), + "cronJob should still include performance", + ); + }); + + it("getToolsForAgentTypes should return cronJob for orchestrator type", async () => { + const tools = await loadTools(); + const toolsForOrchestrator = getToolsForAgentTypes(["orchestrator"], tools); + ok( + toolsForOrchestrator.includes("cronJob"), + "getToolsForAgentTypes(['orchestrator']) should include cronJob", + ); + }); + + it("getToolsForAgentTypes should return cronJob for security-audit type", async () => { + const tools = await loadTools(); + const toolsForSecurity = getToolsForAgentTypes(["security-audit"], tools); + ok( + toolsForSecurity.includes("cronJob"), + "getToolsForAgentTypes(['security-audit']) should include cronJob", + ); + }); + + it("getToolsForAgentTypes should return cronJob for performance type", async () => { + const tools = await loadTools(); + const toolsForPerformance = getToolsForAgentTypes(["performance"], tools); + ok( + toolsForPerformance.includes("cronJob"), + "getToolsForAgentTypes(['performance']) should include cronJob", + ); + }); +});