mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-06-12 19:55:19 +02:00
remove legacy background agents
This commit is contained in:
parent
d8e33afcc8
commit
3073ca8b4c
5 changed files with 130 additions and 660 deletions
|
|
@ -330,6 +330,24 @@ const buildSuggestedTopicExplorePrompt = ({
|
|||
].join('\n')
|
||||
}
|
||||
|
||||
const buildBackgroundAgentSetupPrompt = () => [
|
||||
'Help me set up a background agent.',
|
||||
'In this flow, a background agent is the same thing as a note-based track block. Do not tell me they are separate concepts.',
|
||||
'Do not propose a separate standalone agent, workflow file, or agent-schedule.json setup unless I explicitly ask for that.',
|
||||
'Assume the default home for this setup is knowledge/Tasks/. If that folder does not exist, create it later when setting things up.',
|
||||
'Start with a short, plain-English explanation of what a background agent is.',
|
||||
'Do not make the explanation too terse.',
|
||||
'Give 2 or 3 simple examples of the kinds of things a background agent could help keep updated.',
|
||||
'Do not mention triggers, event-based vs schedule-based behavior, track blocks, skills, note paths, or other internal implementation details unless I ask.',
|
||||
'In the first reply, tell me that you will create this in my Tasks folder by default.',
|
||||
'Do not ask me where it should save or update results unless I explicitly say I want it somewhere else.',
|
||||
'Then ask only what I want it to monitor or update and how often I want it to run.',
|
||||
'Keep it concise and friendly, but not abrupt.',
|
||||
'Do not give me a long taxonomy, a big list of options, or a multi-step breakdown unless I ask for more detail.',
|
||||
'Do not create or modify anything yet.',
|
||||
'If I confirm later, load the tracks skill, check for a matching note under knowledge/Tasks/ first, and create one there if needed.',
|
||||
].join('\n')
|
||||
|
||||
const normalizeUsage = (usage?: Partial<LanguageModelUsage> | null): LanguageModelUsage | null => {
|
||||
if (!usage) return null
|
||||
const hasNumbers = Object.values(usage).some((value) => typeof value === 'number')
|
||||
|
|
@ -4610,6 +4628,9 @@ function App() {
|
|||
<div className="flex-1 min-h-0 flex flex-col overflow-hidden">
|
||||
<BackgroundAgentsView
|
||||
onOpenNote={(path) => navigateToFile(path)}
|
||||
onAddNewBackgroundAgent={() => {
|
||||
submitFromPalette(buildBackgroundAgentSetupPrompt(), null)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : selectedPath && isBaseFilePath(selectedPath) ? (
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { Bot, Loader2 } from 'lucide-react'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { stripKnowledgePrefix, wikiLabel } from '@/lib/wiki-links'
|
||||
import { toast } from '@/lib/toast'
|
||||
|
|
@ -15,6 +16,7 @@ type BackgroundAgentNote = {
|
|||
|
||||
type BackgroundAgentsViewProps = {
|
||||
onOpenNote: (path: string) => void
|
||||
onAddNewBackgroundAgent: () => void
|
||||
}
|
||||
|
||||
function formatDateLabel(iso: string | null): string {
|
||||
|
|
@ -45,7 +47,7 @@ function isKnowledgeMarkdownPath(path: string | undefined): boolean {
|
|||
return typeof path === 'string' && path.startsWith('knowledge/') && path.endsWith('.md')
|
||||
}
|
||||
|
||||
export function BackgroundAgentsView({ onOpenNote }: BackgroundAgentsViewProps) {
|
||||
export function BackgroundAgentsView({ onOpenNote, onAddNewBackgroundAgent }: BackgroundAgentsViewProps) {
|
||||
const [notes, setNotes] = useState<BackgroundAgentNote[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
|
@ -139,114 +141,109 @@ export function BackgroundAgentsView({ onOpenNote }: BackgroundAgentsViewProps)
|
|||
}
|
||||
}, [])
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<Loader2 className="size-5 animate-spin text-muted-foreground" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex h-full flex-col items-center justify-center gap-3 px-8 text-center">
|
||||
<div className="rounded-full bg-muted p-3">
|
||||
<Bot className="size-6 text-muted-foreground" />
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">{error}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (notes.length === 0) {
|
||||
return (
|
||||
<div className="flex h-full flex-col items-center justify-center gap-3 px-8 text-center">
|
||||
<div className="rounded-full bg-muted p-3">
|
||||
<Bot className="size-6 text-muted-foreground" />
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
No notes with background agents yet.
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col overflow-hidden">
|
||||
<div className="shrink-0 border-b border-border px-6 py-5">
|
||||
<div className="flex items-center gap-2">
|
||||
<Bot className="size-5 text-primary" />
|
||||
<h2 className="text-base font-semibold text-foreground">Background agents</h2>
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Bot className="size-5 text-primary" />
|
||||
<h2 className="text-base font-semibold text-foreground">Background agents</h2>
|
||||
</div>
|
||||
<Button type="button" size="sm" onClick={onAddNewBackgroundAgent}>
|
||||
Add new background agent
|
||||
</Button>
|
||||
</div>
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
Notes that contain track blocks. Toggle a note inactive to pause every background agent in it.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-1 overflow-auto p-6">
|
||||
<div className="overflow-hidden rounded-xl border border-border/60 bg-card">
|
||||
<table className="min-w-full border-collapse">
|
||||
<thead>
|
||||
<tr className="border-b border-border/60 bg-muted/30 text-left">
|
||||
<th className="px-4 py-3 text-xs font-medium uppercase tracking-wide text-muted-foreground">Note</th>
|
||||
<th className="px-4 py-3 text-xs font-medium uppercase tracking-wide text-muted-foreground">Created date</th>
|
||||
<th className="px-4 py-3 text-xs font-medium uppercase tracking-wide text-muted-foreground">Last ran</th>
|
||||
<th className="px-4 py-3 text-xs font-medium uppercase tracking-wide text-muted-foreground">State</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{notes.map((note) => {
|
||||
const isUpdating = updatingPaths.has(note.path)
|
||||
return (
|
||||
<tr key={note.path} className="border-b border-border/50 last:border-b-0 hover:bg-muted/20">
|
||||
<td className="px-4 py-3 align-top">
|
||||
<div className="flex min-w-0 flex-col gap-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onOpenNote(note.path)}
|
||||
className="truncate text-left text-sm font-medium text-foreground hover:text-primary"
|
||||
title={note.path}
|
||||
>
|
||||
{wikiLabel(note.path)}
|
||||
</button>
|
||||
<span className="shrink-0 rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground">
|
||||
{note.trackCount} {note.trackCount === 1 ? 'agent' : 'agents'}
|
||||
{loading ? (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<Loader2 className="size-5 animate-spin text-muted-foreground" />
|
||||
</div>
|
||||
) : error ? (
|
||||
<div className="flex h-full flex-col items-center justify-center gap-3 px-8 text-center">
|
||||
<div className="rounded-full bg-muted p-3">
|
||||
<Bot className="size-6 text-muted-foreground" />
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">{error}</p>
|
||||
</div>
|
||||
) : notes.length === 0 ? (
|
||||
<div className="flex h-full flex-col items-center justify-center gap-3 px-8 text-center">
|
||||
<div className="rounded-full bg-muted p-3">
|
||||
<Bot className="size-6 text-muted-foreground" />
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
No notes with background agents yet.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="overflow-hidden rounded-xl border border-border/60 bg-card">
|
||||
<table className="min-w-full border-collapse">
|
||||
<thead>
|
||||
<tr className="border-b border-border/60 bg-muted/30 text-left">
|
||||
<th className="px-4 py-3 text-xs font-medium uppercase tracking-wide text-muted-foreground">Note</th>
|
||||
<th className="px-4 py-3 text-xs font-medium uppercase tracking-wide text-muted-foreground">Created date</th>
|
||||
<th className="px-4 py-3 text-xs font-medium uppercase tracking-wide text-muted-foreground">Last ran</th>
|
||||
<th className="px-4 py-3 text-xs font-medium uppercase tracking-wide text-muted-foreground">State</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{notes.map((note) => {
|
||||
const isUpdating = updatingPaths.has(note.path)
|
||||
return (
|
||||
<tr key={note.path} className="border-b border-border/50 last:border-b-0 hover:bg-muted/20">
|
||||
<td className="px-4 py-3 align-top">
|
||||
<div className="flex min-w-0 flex-col gap-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onOpenNote(note.path)}
|
||||
className="truncate text-left text-sm font-medium text-foreground hover:text-primary"
|
||||
title={note.path}
|
||||
>
|
||||
{wikiLabel(note.path)}
|
||||
</button>
|
||||
<span className="shrink-0 rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground">
|
||||
{note.trackCount} {note.trackCount === 1 ? 'agent' : 'agents'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="truncate text-xs text-muted-foreground">
|
||||
{stripKnowledgePrefix(note.path)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-foreground/80">
|
||||
{formatDateLabel(note.createdAt)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-foreground/80">
|
||||
{formatDateTimeLabel(note.lastRunAt)}
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="flex items-center gap-3">
|
||||
{isUpdating ? (
|
||||
<Loader2 className="size-4 animate-spin text-muted-foreground" />
|
||||
) : (
|
||||
<span className="size-4 shrink-0" aria-hidden="true" />
|
||||
)}
|
||||
<Switch
|
||||
checked={note.isActive}
|
||||
onCheckedChange={(checked) => { void handleToggleState(note, checked) }}
|
||||
disabled={isUpdating}
|
||||
/>
|
||||
<span className="min-w-16 text-xs font-medium text-foreground/80">
|
||||
{note.isActive ? 'Active' : 'Inactive'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="truncate text-xs text-muted-foreground">
|
||||
{stripKnowledgePrefix(note.path)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-foreground/80">
|
||||
{formatDateLabel(note.createdAt)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-foreground/80">
|
||||
{formatDateTimeLabel(note.lastRunAt)}
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="flex items-center gap-3">
|
||||
{isUpdating ? (
|
||||
<Loader2 className="size-4 animate-spin text-muted-foreground" />
|
||||
) : (
|
||||
<span className="size-4 shrink-0" aria-hidden="true" />
|
||||
)}
|
||||
<Switch
|
||||
checked={note.isActive}
|
||||
onCheckedChange={(checked) => { void handleToggleState(note, checked) }}
|
||||
disabled={isUpdating}
|
||||
/>
|
||||
<span className="min-w-16 text-xs font-medium text-foreground/80">
|
||||
{note.isActive ? 'Active' : 'Inactive'}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,555 +0,0 @@
|
|||
export const skill = String.raw`
|
||||
# Background Agents
|
||||
|
||||
Load this skill whenever a user wants to inspect, create, edit, or schedule background agents inside the Rowboat workspace.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
**IMPORTANT**: In the CLI, there are NO separate "workflow" files. Everything is an agent.
|
||||
|
||||
- **All definitions live in ` + "`agents/*.md`" + `** - Markdown files with YAML frontmatter
|
||||
- Agents configure a model, tools (in frontmatter), and instructions (in the body)
|
||||
- Tools can be: builtin (like ` + "`executeCommand`" + `), MCP integrations, or **other agents**
|
||||
- **"Workflows" are just agents that orchestrate other agents** by having them as tools
|
||||
- **Background agents run on schedules** defined in ` + "`config/agent-schedule.json`" + ` within the workspace root
|
||||
|
||||
## How multi-agent workflows work
|
||||
|
||||
1. **Create an orchestrator agent** that has other agents in its ` + "`tools`" + `
|
||||
2. **Schedule the orchestrator** in agent-schedule.json (see Scheduling section below)
|
||||
3. The orchestrator calls other agents as tools when needed
|
||||
4. Data flows through tool call parameters and responses
|
||||
|
||||
## Scheduling Background Agents
|
||||
|
||||
Background agents run automatically based on schedules defined in ` + "`config/agent-schedule.json`" + ` in the workspace root.
|
||||
|
||||
### Schedule Configuration File
|
||||
|
||||
` + "```json" + `
|
||||
{
|
||||
"agents": {
|
||||
"agent_name": {
|
||||
"schedule": { ... },
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
` + "```" + `
|
||||
|
||||
### Schedule Types
|
||||
|
||||
**IMPORTANT: All times are in local time** (the timezone of the machine running Rowboat).
|
||||
|
||||
**1. Cron Schedule** - Runs at exact times defined by cron expression
|
||||
` + "```json" + `
|
||||
{
|
||||
"schedule": {
|
||||
"type": "cron",
|
||||
"expression": "0 8 * * *"
|
||||
},
|
||||
"enabled": true
|
||||
}
|
||||
` + "```" + `
|
||||
|
||||
Common cron expressions:
|
||||
- ` + "`*/5 * * * *`" + ` - Every 5 minutes
|
||||
- ` + "`0 8 * * *`" + ` - Every day at 8am
|
||||
- ` + "`0 9 * * 1`" + ` - Every Monday at 9am
|
||||
- ` + "`0 0 1 * *`" + ` - First day of every month at midnight
|
||||
|
||||
**2. Window Schedule** - Runs once during a time window
|
||||
` + "```json" + `
|
||||
{
|
||||
"schedule": {
|
||||
"type": "window",
|
||||
"cron": "0 0 * * *",
|
||||
"startTime": "08:00",
|
||||
"endTime": "10:00"
|
||||
},
|
||||
"enabled": true
|
||||
}
|
||||
` + "```" + `
|
||||
|
||||
The agent will run once at a random time within the window. Use this when you want flexibility (e.g., "sometime in the morning" rather than "exactly at 8am").
|
||||
|
||||
**3. Once Schedule** - Runs exactly once at a specific time
|
||||
` + "```json" + `
|
||||
{
|
||||
"schedule": {
|
||||
"type": "once",
|
||||
"runAt": "2024-02-05T10:30:00"
|
||||
},
|
||||
"enabled": true
|
||||
}
|
||||
` + "```" + `
|
||||
|
||||
Use this for one-time tasks like migrations or setup scripts. The ` + "`runAt`" + ` is in local time (no Z suffix).
|
||||
|
||||
### Starting Message
|
||||
|
||||
You can specify a ` + "`startingMessage`" + ` that gets sent to the agent when it starts. If not provided, defaults to ` + "`\"go\"`" + `.
|
||||
|
||||
` + "```json" + `
|
||||
{
|
||||
"schedule": { "type": "cron", "expression": "0 8 * * *" },
|
||||
"enabled": true,
|
||||
"startingMessage": "Please summarize my emails from the last 24 hours"
|
||||
}
|
||||
` + "```" + `
|
||||
|
||||
### Description
|
||||
|
||||
You can add a ` + "`description`" + ` field to describe what the agent does. This is displayed in the UI.
|
||||
|
||||
` + "```json" + `
|
||||
{
|
||||
"schedule": { "type": "cron", "expression": "0 8 * * *" },
|
||||
"enabled": true,
|
||||
"description": "Summarizes emails and calendar events every morning"
|
||||
}
|
||||
` + "```" + `
|
||||
|
||||
### Complete Schedule Example
|
||||
|
||||
` + "```json" + `
|
||||
{
|
||||
"agents": {
|
||||
"daily_digest": {
|
||||
"schedule": {
|
||||
"type": "cron",
|
||||
"expression": "0 8 * * *"
|
||||
},
|
||||
"enabled": true,
|
||||
"description": "Daily email and calendar summary",
|
||||
"startingMessage": "Summarize my emails and calendar for today"
|
||||
},
|
||||
"morning_briefing": {
|
||||
"schedule": {
|
||||
"type": "window",
|
||||
"cron": "0 0 * * *",
|
||||
"startTime": "07:00",
|
||||
"endTime": "09:00"
|
||||
},
|
||||
"enabled": true,
|
||||
"description": "Morning news and updates briefing"
|
||||
},
|
||||
"one_time_setup": {
|
||||
"schedule": {
|
||||
"type": "once",
|
||||
"runAt": "2024-12-01T12:00:00"
|
||||
},
|
||||
"enabled": true,
|
||||
"description": "One-time data migration task"
|
||||
}
|
||||
}
|
||||
}
|
||||
` + "```" + `
|
||||
|
||||
### Schedule State (Read-Only)
|
||||
|
||||
**IMPORTANT: Do NOT modify ` + "`agent-schedule-state.json`" + `** - it is managed automatically by the background runner.
|
||||
|
||||
The runner automatically tracks execution state in ` + "`config/agent-schedule-state.json`" + ` in the workspace root:
|
||||
- ` + "`status`" + `: scheduled, running, finished, failed, triggered (for once-schedules)
|
||||
- ` + "`lastRunAt`" + `: When the agent last ran
|
||||
- ` + "`nextRunAt`" + `: When the agent will run next
|
||||
- ` + "`lastError`" + `: Error message if the last run failed
|
||||
- ` + "`runCount`" + `: Total number of runs
|
||||
|
||||
When you add an agent to ` + "`agent-schedule.json`" + `, the runner will automatically create and manage its state entry. You only need to edit ` + "`agent-schedule.json`" + `.
|
||||
|
||||
## Agent File Format
|
||||
|
||||
Agent files are **Markdown files with YAML frontmatter**. The frontmatter contains configuration (model, tools), and the body contains the instructions.
|
||||
|
||||
### Basic Structure
|
||||
` + "```markdown" + `
|
||||
---
|
||||
model: gpt-5.1
|
||||
tools:
|
||||
tool_key:
|
||||
type: builtin
|
||||
name: tool_name
|
||||
---
|
||||
# Instructions
|
||||
|
||||
Your detailed instructions go here in Markdown format.
|
||||
` + "```" + `
|
||||
|
||||
### Frontmatter Fields
|
||||
- ` + "`model`" + `: (OPTIONAL) Model to use (e.g., 'gpt-5.1', 'claude-sonnet-4-5')
|
||||
- ` + "`provider`" + `: (OPTIONAL) Provider alias from models.json
|
||||
- ` + "`tools`" + `: (OPTIONAL) Object containing tool definitions
|
||||
|
||||
### Instructions (Body)
|
||||
The Markdown body after the frontmatter contains the agent's instructions. Use standard Markdown formatting.
|
||||
|
||||
### Naming Rules
|
||||
- Agent filename determines the agent name (without .md extension)
|
||||
- Example: ` + "`summariser_agent.md`" + ` creates an agent named "summariser_agent"
|
||||
- Use lowercase with underscores for multi-word names
|
||||
- No spaces or special characters in names
|
||||
- **The agent name in agent-schedule.json must match the filename** (without .md)
|
||||
|
||||
### Agent Format Example
|
||||
` + "```markdown" + `
|
||||
---
|
||||
model: gpt-5.1
|
||||
tools:
|
||||
search:
|
||||
type: mcp
|
||||
name: firecrawl_search
|
||||
description: Search the web
|
||||
mcpServerName: firecrawl
|
||||
inputSchema:
|
||||
type: object
|
||||
properties:
|
||||
query:
|
||||
type: string
|
||||
description: Search query
|
||||
required:
|
||||
- query
|
||||
---
|
||||
# Web Search Agent
|
||||
|
||||
You are a web search agent. When asked a question:
|
||||
|
||||
1. Use the search tool to find relevant information
|
||||
2. Summarize the results clearly
|
||||
3. Cite your sources
|
||||
|
||||
Be concise and accurate.
|
||||
` + "```" + `
|
||||
|
||||
## Tool Types & Schemas
|
||||
|
||||
Tools in agents must follow one of three types. Each has specific required fields.
|
||||
|
||||
### 1. Builtin Tools
|
||||
Internal Rowboat tools (executeCommand, file operations, MCP queries, etc.)
|
||||
|
||||
**YAML Schema:**
|
||||
` + "```yaml" + `
|
||||
tool_key:
|
||||
type: builtin
|
||||
name: tool_name
|
||||
` + "```" + `
|
||||
|
||||
**Required fields:**
|
||||
- ` + "`type`" + `: Must be "builtin"
|
||||
- ` + "`name`" + `: Builtin tool name (e.g., "executeCommand", "workspace-readFile")
|
||||
|
||||
**Example:**
|
||||
` + "```yaml" + `
|
||||
bash:
|
||||
type: builtin
|
||||
name: executeCommand
|
||||
` + "```" + `
|
||||
|
||||
**Available builtin tools:**
|
||||
- ` + "`executeCommand`" + ` - Execute shell commands
|
||||
- ` + "`workspace-readFile`" + `, ` + "`workspace-writeFile`" + `, ` + "`workspace-remove`" + ` - File operations
|
||||
- ` + "`workspace-readdir`" + `, ` + "`workspace-exists`" + `, ` + "`workspace-stat`" + ` - Directory operations
|
||||
- ` + "`workspace-mkdir`" + `, ` + "`workspace-rename`" + `, ` + "`workspace-copy`" + ` - File/directory management
|
||||
- ` + "`analyzeAgent`" + ` - Analyze agent structure
|
||||
- ` + "`addMcpServer`" + `, ` + "`listMcpServers`" + `, ` + "`listMcpTools`" + ` - MCP management
|
||||
- ` + "`loadSkill`" + ` - Load skill guidance
|
||||
|
||||
### 2. MCP Tools
|
||||
Tools from external MCP servers (APIs, databases, web scraping, etc.)
|
||||
|
||||
**YAML Schema:**
|
||||
` + "```yaml" + `
|
||||
tool_key:
|
||||
type: mcp
|
||||
name: tool_name_from_server
|
||||
description: What the tool does
|
||||
mcpServerName: server_name_from_config
|
||||
inputSchema:
|
||||
type: object
|
||||
properties:
|
||||
param:
|
||||
type: string
|
||||
description: Parameter description
|
||||
required:
|
||||
- param
|
||||
` + "```" + `
|
||||
|
||||
**Required fields:**
|
||||
- ` + "`type`" + `: Must be "mcp"
|
||||
- ` + "`name`" + `: Exact tool name from MCP server
|
||||
- ` + "`description`" + `: What the tool does (helps agent understand when to use it)
|
||||
- ` + "`mcpServerName`" + `: Server name from config/mcp.json
|
||||
- ` + "`inputSchema`" + `: Full JSON Schema object for tool parameters
|
||||
|
||||
**Example:**
|
||||
` + "```yaml" + `
|
||||
search:
|
||||
type: mcp
|
||||
name: firecrawl_search
|
||||
description: Search the web
|
||||
mcpServerName: firecrawl
|
||||
inputSchema:
|
||||
type: object
|
||||
properties:
|
||||
query:
|
||||
type: string
|
||||
description: Search query
|
||||
required:
|
||||
- query
|
||||
` + "```" + `
|
||||
|
||||
**Important:**
|
||||
- Use ` + "`listMcpTools`" + ` to get the exact inputSchema from the server
|
||||
- Copy the schema exactly—don't modify property types or structure
|
||||
- Only include ` + "`required`" + ` array if parameters are mandatory
|
||||
|
||||
### 3. Agent Tools (for chaining agents)
|
||||
Reference other agents as tools to build multi-agent workflows
|
||||
|
||||
**YAML Schema:**
|
||||
` + "```yaml" + `
|
||||
tool_key:
|
||||
type: agent
|
||||
name: target_agent_name
|
||||
` + "```" + `
|
||||
|
||||
**Required fields:**
|
||||
- ` + "`type`" + `: Must be "agent"
|
||||
- ` + "`name`" + `: Name of the target agent (must exist in agents/ directory)
|
||||
|
||||
**Example:**
|
||||
` + "```yaml" + `
|
||||
summariser:
|
||||
type: agent
|
||||
name: summariser_agent
|
||||
` + "```" + `
|
||||
|
||||
**How it works:**
|
||||
- Use ` + "`type: agent`" + ` to call other agents as tools
|
||||
- The target agent will be invoked with the parameters you pass
|
||||
- Results are returned as tool output
|
||||
- This is how you build multi-agent workflows
|
||||
- The referenced agent file must exist (e.g., ` + "`agents/summariser_agent.md`" + `)
|
||||
|
||||
## Complete Multi-Agent Workflow Example
|
||||
|
||||
**Email digest workflow** - This is all done through agents calling other agents:
|
||||
|
||||
**1. Task-specific agent** (` + "`agents/email_reader.md`" + `):
|
||||
` + "```markdown" + `
|
||||
---
|
||||
model: gpt-5.1
|
||||
tools:
|
||||
read_file:
|
||||
type: builtin
|
||||
name: workspace-readFile
|
||||
list_dir:
|
||||
type: builtin
|
||||
name: workspace-readdir
|
||||
---
|
||||
# Email Reader Agent
|
||||
|
||||
Read emails from the gmail_sync folder and extract key information.
|
||||
Look for unread or recent emails and summarize the sender, subject, and key points.
|
||||
Don't ask for human input.
|
||||
` + "```" + `
|
||||
|
||||
**2. Agent that delegates to other agents** (` + "`agents/daily_summary.md`" + `):
|
||||
` + "```markdown" + `
|
||||
---
|
||||
model: gpt-5.1
|
||||
tools:
|
||||
email_reader:
|
||||
type: agent
|
||||
name: email_reader
|
||||
write_file:
|
||||
type: builtin
|
||||
name: workspace-writeFile
|
||||
---
|
||||
# Daily Summary Agent
|
||||
|
||||
1. Use the email_reader tool to get email summaries
|
||||
2. Create a consolidated daily digest
|
||||
3. Save the digest to ~/Desktop/daily_digest.md
|
||||
|
||||
Don't ask for human input.
|
||||
` + "```" + `
|
||||
|
||||
Note: The output path (` + "`~/Desktop/daily_digest.md`" + `) is hardcoded in the instructions. When creating agents that output files, always ask the user where they want files saved and include the full path in the agent instructions.
|
||||
|
||||
**3. Orchestrator agent** (` + "`agents/morning_briefing.md`" + `):
|
||||
` + "```markdown" + `
|
||||
---
|
||||
model: gpt-5.1
|
||||
tools:
|
||||
daily_summary:
|
||||
type: agent
|
||||
name: daily_summary
|
||||
search:
|
||||
type: mcp
|
||||
name: search
|
||||
mcpServerName: exa
|
||||
description: Search the web for news
|
||||
inputSchema:
|
||||
type: object
|
||||
properties:
|
||||
query:
|
||||
type: string
|
||||
description: Search query
|
||||
---
|
||||
# Morning Briefing Workflow
|
||||
|
||||
Create a morning briefing:
|
||||
|
||||
1. Get email digest using daily_summary
|
||||
2. Search for relevant news using the search tool
|
||||
3. Compile a comprehensive morning briefing
|
||||
|
||||
Execute these steps in sequence. Don't ask for human input.
|
||||
` + "```" + `
|
||||
|
||||
**4. Schedule the workflow** in ` + "`config/agent-schedule.json`" + `:
|
||||
` + "```json" + `
|
||||
{
|
||||
"agents": {
|
||||
"morning_briefing": {
|
||||
"schedule": {
|
||||
"type": "cron",
|
||||
"expression": "0 7 * * *"
|
||||
},
|
||||
"enabled": true,
|
||||
"startingMessage": "Create my morning briefing for today"
|
||||
}
|
||||
}
|
||||
}
|
||||
` + "```" + `
|
||||
|
||||
This schedules the morning briefing workflow to run every day at 7am local time.
|
||||
|
||||
## Naming and organization rules
|
||||
- **All agents live in ` + "`agents/*.md`" + `** - Markdown files with YAML frontmatter
|
||||
- Agent filename (without .md) becomes the agent name
|
||||
- When referencing an agent as a tool, use its filename without extension
|
||||
- When scheduling an agent, use its filename without extension in agent-schedule.json
|
||||
- Use relative paths (no \${BASE_DIR} prefixes) when giving examples to users
|
||||
|
||||
## Best practices for background agents
|
||||
1. **Single responsibility**: Each agent should do one specific thing well
|
||||
2. **Clear delegation**: Agent instructions should explicitly say when to call other agents
|
||||
3. **Autonomous operation**: Add "Don't ask for human input" for background agents
|
||||
4. **Data passing**: Make it clear what data to extract and pass between agents
|
||||
5. **Tool naming**: Use descriptive tool keys (e.g., "summariser", "fetch_data", "analyze")
|
||||
6. **Orchestration**: Create a top-level agent that coordinates the workflow
|
||||
7. **Scheduling**: Use appropriate schedule types - cron for recurring, window for flexible timing, once for one-time tasks
|
||||
8. **Error handling**: Background agents should handle errors gracefully since there's no human to intervene
|
||||
9. **Avoid executeCommand**: Do NOT attach ` + "`executeCommand`" + ` to background agents as it poses security risks when running unattended. Instead, use the specific builtin tools needed (` + "`workspace-readFile`" + `, ` + "`workspace-writeFile`" + `, etc.) or MCP tools for external integrations
|
||||
10. **File output paths**: When creating an agent that outputs files, ASK the user where the file should be stored (default to Desktop: ` + "`~/Desktop`" + `). Then hardcode the full output path in the agent's instructions so it knows exactly where to write files. Example instruction: "Save the output to /Users/username/Desktop/daily_report.md"
|
||||
|
||||
## Validation & Best Practices
|
||||
|
||||
### CRITICAL: Schema Compliance
|
||||
- Agent files MUST be valid Markdown with YAML frontmatter
|
||||
- Agent filename (without .md) becomes the agent name
|
||||
- Tools in frontmatter MUST have valid ` + "`type`" + ` ("builtin", "mcp", or "agent")
|
||||
- MCP tools MUST have all required fields: name, description, mcpServerName, inputSchema
|
||||
- Agent tools MUST reference existing agent files
|
||||
- Invalid agents will fail to load and prevent workflow execution
|
||||
|
||||
### File Creation/Update Process
|
||||
1. When creating an agent, use ` + "`workspace-writeFile`" + ` with valid Markdown + YAML frontmatter
|
||||
2. When updating an agent, read it first with ` + "`workspace-readFile`" + `, modify, then use ` + "`workspace-writeFile`" + `
|
||||
3. Validate YAML syntax in frontmatter before writing—malformed YAML breaks the agent
|
||||
4. **Quote strings containing colons** (e.g., ` + "`description: \"Default: 8\"`" + ` not ` + "`description: Default: 8`" + `)
|
||||
5. Test agent loading after creation/update by using ` + "`analyzeAgent`" + `
|
||||
|
||||
### Common Validation Errors to Avoid
|
||||
|
||||
❌ **WRONG - Missing frontmatter delimiters:**
|
||||
` + "```markdown" + `
|
||||
model: gpt-5.1
|
||||
# My Agent
|
||||
Instructions here
|
||||
` + "```" + `
|
||||
|
||||
❌ **WRONG - Invalid YAML indentation:**
|
||||
` + "```markdown" + `
|
||||
---
|
||||
tools:
|
||||
bash:
|
||||
type: builtin
|
||||
---
|
||||
` + "```" + `
|
||||
(bash should be indented under tools)
|
||||
|
||||
❌ **WRONG - Invalid tool type:**
|
||||
` + "```yaml" + `
|
||||
tools:
|
||||
tool1:
|
||||
type: custom
|
||||
name: something
|
||||
` + "```" + `
|
||||
(type must be builtin, mcp, or agent)
|
||||
|
||||
❌ **WRONG - Unquoted strings containing colons:**
|
||||
` + "```yaml" + `
|
||||
tools:
|
||||
search:
|
||||
description: Number of results (default: 8)
|
||||
` + "```" + `
|
||||
(Strings with colons must be quoted: ` + "`description: \"Number of results (default: 8)\"`" + `)
|
||||
|
||||
❌ **WRONG - MCP tool missing required fields:**
|
||||
` + "```yaml" + `
|
||||
tools:
|
||||
search:
|
||||
type: mcp
|
||||
name: firecrawl_search
|
||||
` + "```" + `
|
||||
(Missing: description, mcpServerName, inputSchema)
|
||||
|
||||
✅ **CORRECT - Minimal valid agent** (` + "`agents/simple_agent.md`" + `):
|
||||
` + "```markdown" + `
|
||||
---
|
||||
model: gpt-5.1
|
||||
---
|
||||
# Simple Agent
|
||||
|
||||
Do simple tasks as instructed.
|
||||
` + "```" + `
|
||||
|
||||
✅ **CORRECT - Agent with MCP tool** (` + "`agents/search_agent.md`" + `):
|
||||
` + "```markdown" + `
|
||||
---
|
||||
model: gpt-5.1
|
||||
tools:
|
||||
search:
|
||||
type: mcp
|
||||
name: firecrawl_search
|
||||
description: Search the web
|
||||
mcpServerName: firecrawl
|
||||
inputSchema:
|
||||
type: object
|
||||
properties:
|
||||
query:
|
||||
type: string
|
||||
---
|
||||
# Search Agent
|
||||
|
||||
Use the search tool to find information on the web.
|
||||
` + "```" + `
|
||||
|
||||
## Capabilities checklist
|
||||
1. Explore ` + "`agents/`" + ` directory to understand existing agents before editing
|
||||
2. Read existing agents with ` + "`workspace-readFile`" + ` before making changes
|
||||
3. Validate YAML frontmatter syntax before creating/updating agents
|
||||
4. Use ` + "`analyzeAgent`" + ` to verify agent structure after creation/update
|
||||
5. When creating multi-agent workflows, create an orchestrator agent
|
||||
6. Add other agents as tools with ` + "`type: agent`" + ` for chaining
|
||||
7. Use ` + "`listMcpServers`" + ` and ` + "`listMcpTools`" + ` when adding MCP integrations
|
||||
8. Configure schedules in ` + "`config/agent-schedule.json`" + ` (ONLY edit this file, NOT the state file)
|
||||
9. Confirm work done and outline next steps once changes are complete
|
||||
`;
|
||||
|
||||
export default skill;
|
||||
|
|
@ -7,7 +7,6 @@ import draftEmailsSkill from "./draft-emails/skill.js";
|
|||
import mcpIntegrationSkill from "./mcp-integration/skill.js";
|
||||
import meetingPrepSkill from "./meeting-prep/skill.js";
|
||||
import organizeFilesSkill from "./organize-files/skill.js";
|
||||
import backgroundAgentsSkill from "./background-agents/skill.js";
|
||||
import createPresentationsSkill from "./create-presentations/skill.js";
|
||||
|
||||
import appNavigationSkill from "./app-navigation/skill.js";
|
||||
|
|
@ -65,12 +64,6 @@ const definitions: SkillDefinition[] = [
|
|||
summary: "Find, organize, and tidy up files on the user's machine. Move files to folders, clean up Desktop/Downloads, locate specific files.",
|
||||
content: organizeFilesSkill,
|
||||
},
|
||||
{
|
||||
id: "background-agents",
|
||||
title: "Background Agents",
|
||||
summary: "Creating, editing, and scheduling background agents. Configure schedules in agent-schedule.json and build multi-agent workflows.",
|
||||
content: backgroundAgentsSkill,
|
||||
},
|
||||
{
|
||||
id: "builtin-tools",
|
||||
title: "Builtin Tools Reference",
|
||||
|
|
|
|||
|
|
@ -349,6 +349,20 @@ In that flow:
|
|||
6. Keep the surrounding note scaffolding minimal but useful. The track block should be the core of the note.
|
||||
7. If the target folder is one of the structured knowledge folders (` + "`" + `knowledge/People/` + "`" + `, ` + "`" + `knowledge/Organizations/` + "`" + `, ` + "`" + `knowledge/Projects/` + "`" + `, ` + "`" + `knowledge/Topics/` + "`" + `), mirror the local note style by quickly checking a nearby note or config before writing if needed.
|
||||
|
||||
### Background agent setup flow
|
||||
|
||||
Sometimes the user arrives from the Background agents panel and wants help creating a new background agent without naming a note yet.
|
||||
|
||||
In this flow, treat "background agent" and "track block" as the same feature. The user-facing term can stay "background agent", but the implementation is a track block inside a note. Do **not** claim these are different systems, and do **not** redirect the user toward standalone agent files or ` + "`" + `agent-schedule.json` + "`" + ` unless they explicitly ask for that architecture.
|
||||
|
||||
In that flow:
|
||||
1. On the first turn, **do not create or modify anything yet**. Briefly explain what you can set up, say you will put it in ` + "`" + `knowledge/Tasks/` + "`" + ` by default, and ask what it should monitor plus how often it should run.
|
||||
2. **Do not** ask the user where the results should live unless they explicitly said they want a different folder or there is a real ambiguity you cannot resolve.
|
||||
3. If the user clearly confirms later, treat ` + "`" + `knowledge/Tasks/` + "`" + ` as the default target folder.
|
||||
4. Before creating a new note there, search ` + "`" + `knowledge/Tasks/` + "`" + ` for an existing matching note and update it if one already exists.
|
||||
5. If ` + "`" + `knowledge/Tasks/` + "`" + ` does not exist, create it as part of setup instead of bouncing back to ask.
|
||||
6. Keep the surrounding note scaffolding minimal but useful. The track block should be the core of the note.
|
||||
|
||||
## The Exact Text to Insert
|
||||
|
||||
Write it verbatim like this (including the blank line between fence and target):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue