# Automation ## Overview The `/automation` route is the hub for managing AI agent capabilities: skills, tools, tasks, and processes. Each capability is a markdown file (with YAML frontmatter) stored on the filesystem. There is no relational database for automation data. The page uses a resizable `WorkspaceView` layout with three panels: a sidebar to pick categories, a right panel for browsing/viewing, and an optional bottom-right chat panel for AI-assisted editing. ``` +------------------+------------------------------------------+ | Sidebar (20%) | Right Panel (80%) | | | - CapabilityList (browse) | | 8 categories | - CapabilityDetailView (selected item) | | | - New* form (creating) | | +------------------------------------------+ | | AutomationEditChat (when editing, 50%) | +------------------+------------------------------------------+ ``` ## Route & Entry Point Defined in `App.tsx`: ```tsx } /> } /> // stub, not implemented ``` Entry component: `index.tsx` (`Automation`). Manages the `WorkspaceView` layout and dynamically adds/removes the chat panel based on `selection.editing`. ## Capability Categories | Category | Kind | Backend Route | Status | |----------|------|---------------|--------| | Skills | `Skill` | `GET/POST/DELETE /api/skills` | Working | | Tools | `Tool` | `GET/POST/DELETE /api/tools` | Working | | Tasks | `Task` | `GET/POST/DELETE /api/tasks` | Working | | Processes | `Process` | `GET/POST/DELETE /api/processes` | Working | | Pipelines | `Pipeline` | None | Placeholder (404) | | Workflows | `Workflow` | None | Placeholder (404) | | Crons | `Cron` | None | Placeholder (404) | | Services | `Service` | None | Placeholder (404) | Defined as `capabilityItems` in `AutomationSidebar.tsx`. ## State Management All cross-panel communication uses a single shared channel: ```ts usePanelChannel('automation:selected-capability', null) ``` `AutomationSelection` (defined in `AutomationRightPanel.tsx`): ```ts type AutomationSelection = { kind: string; // 'Skill' | 'Task' | 'Tool' | 'Process' | etc. endpoint: string; // '/skills' | '/tasks' | etc. queryKey: string; // React Query cache key dirName: string; // '' = list view, non-empty = detail view isNew?: boolean; // just created, AI chat opens in creation mode editing?: boolean; // chat panel is open creating?: boolean; // New* form is shown description?: string; // passed to AI as initial context } | null; ``` Layout state persisted via `useDashboardState('screens/automation')`. ## Components ### `AutomationSidebar.tsx` Left nav with the 8 category buttons. Clicking sets selection to `{ kind, endpoint, queryKey, dirName: '' }` (list view). ### `AutomationRightPanel.tsx` Routes to one of three views based on selection state: - `creating === true` -> `New*` form (mapped via `newComponentMap[kind]`) - `dirName === ''` -> `CapabilityList` (browse items) - `dirName !== ''` -> `CapabilityDetailView` (detail for specific item) ### `CapabilityList.tsx` Fetches items from `GET {endpoint}`, renders filterable list. Has `+` button to set `creating: true`. ### `CapabilityDetailView.tsx` Shows a selected capability's detail (frontmatter + markdown body). Header actions: - **Back arrow** - returns to list view - **Run** (tasks only) - parses `inputs:` from frontmatter YAML, opens `TaskRunnerModal` - **Edit** - toggles `selection.editing` to open/close chat panel - **Delete** - confirmation dialog, then `DELETE {endpoint}/{dirName}` Task input parsing (`parseInputs`) supports: `string`, `number`, `boolean`, `select` types. ### `AutomationEditChat.tsx` Bottom chat panel for AI-assisted editing. Uses `CapabilityChat` from `CapabilityPage.tsx`. Features: - Delete chat history button (`DELETE {endpoint}/{dirName}/chat`) - Close button (sets `editing: false`) - On AI response end, invalidates both individual and list query caches ### `New*.tsx` (8 components) `NewTask`, `NewSkill`, `NewTool`, `NewProcess`, `NewPipeline`, `NewCron`, `NewService`, `NewWorkflow`. All structurally identical: 1. Name + description form 2. `POST {endpoint}` with `{ name }` 3. On success: transitions to detail view with `isNew: true, editing: true` (opens AI chat in creation mode) ## Shared Components (`CapabilityPage.tsx`) Located at `../CapabilityPage.tsx`. Exports used by automation: - **`CapabilityChat`** - AI chat wired to Pi agent via `usePiChat()` + `EmbeddableChat`. Injects `promptFrontmatter` prefix before every message. For `isNew === true`, uses rich creation guides (`buildTaskCreationPrefix`, `buildSkillCreationPrefix`, `buildToolCreationPrefix`). - **`FrontmatterBlock`** - Collapsible YAML frontmatter display. - **`CapabilityDetail`** type - `{ dirName, name, description, scope, body, rawFrontmatter, filePath, chatSessionId }`. - **`CapabilityPage`** - Legacy standalone two-panel page (used by `/skills`, `/tasks`, `/processes` routes, not by `/automation`). ## Backend All four working routers (`skills`, `tools`, `tasks`, `processes`) follow the exact same pattern. Registered on the protected router in `hono.ts`. ### API Endpoints (per capability type) | Method | Path | Description | |--------|------|-------------| | `GET` | `/{type}` | List all items (native + global + user merged) | | `GET` | `/{type}/:name` | Get detail: frontmatter, body, rawYaml, filePath, chatSessionId | | `POST` | `/{type}` | Create new item (dir + stub `.md`) | | `DELETE` | `/{type}/:name` | Delete item directory | | `GET` | `/{type}/:name/chat` | Get saved chat messages | | `PUT` | `/{type}/:name/chat` | Save chat messages + session meta | | `DELETE` | `/{type}/:name/chat` | Delete chat directory | Route files: - `src/servers/api/skills/skills.ts` - `src/servers/api/tools/tools.ts` - `src/servers/api/tasks/tasks.ts` (also parses `trigger:` block from frontmatter) - `src/servers/api/processes/processes.ts` ### Task Logs | Method | Path | Description | |--------|------|-------------| | `GET` | `/task-logs` | List all log metadata (newest first) | | `GET` | `/task-logs/:filename` | Get full log with messages | Route file: `src/servers/api/task-logs/task-logs.ts` ## Storage Model Everything is stored on the filesystem. No database tables. ``` DATA_PATH/ skills/ <- global scope {skill-name}/ SKILL.md <- frontmatter + markdown body chat/ meta.json <- { id: sessionId } messages.json <- ChatMessage[] tasks/ <- global scope {task-name}/TASK.md tools/ <- global scope {tool-name}/TOOL.md processes/ <- global scope {process-name}/PROCESS.md {user-email}/ <- user scope skills/ tasks/ tools/ processes/ logs/tasks/ <- task execution logs {timestamp}-{dirName}.json SEED_PATH/ <- native scope (read-only, shipped with app) skills/ (sharp, whisper-cpp, google-mail-api, ffmpeg, mutagen, fizzy-cli, mlxaudio) tasks/ (convert-to-mp3, sync-gmail-inbox, tiktok-trends, transcribe-audio-file, ...) tools/ (apify, browser, email-db, ffmpeg, gmail, ocr, web-fetch, web-search, ...) ``` Path helpers: `src/servers/data-path.ts` (`getNativeSkillsDir`, `getGlobalSkillsDir`, `getUserSkillsDir`, etc.) ### Scope & Permissions Three tiers with override resolution: **user > global > native**. - `native`: seed directory, read-only, shipped with the app - `global`: shared data directory, writable by Super Admin - `user`: per-user directory, writable by the owning user Regular users (`Member`) can only create/delete `user`-scoped items. `Super Admin` can also create/delete `global` items. ## Markdown File Formats ### TASK.md ```yaml --- name: Task Name description: What the task does. version: 1 author: pastilhas tags: [tag1, tag2] skills: [skill-name] # optional, skills the agent can consult tools: [tool_name] # optional, tools the agent can call trigger: # optional, when omitted: only runnable from Automation page - type: file extensions: [mp3, flac] - type: directory inputs: # optional, parameters the user fills in before running - name: param_name type: string # string | number | boolean | select required: true default: some value --- (Agent instructions in markdown) ``` ### SKILL.md ```yaml --- name: skill-name description: When to use this skill. --- (Knowledge base / reference documentation in markdown) ``` ### TOOL.md ```yaml --- name: tool_name label: Tool Display Name description: What the tool does. language: typescript # typescript | bash | python inputs: param_name: type: string # string | number | boolean | enum | object description: What this parameter is for. optional: true sensitive: true --- (Usage notes, output format, examples in markdown) ``` ### PROCESS.md Same structure as SKILL.md (name + description frontmatter, markdown body). ## Task Execution Flow 1. User clicks "Run" on a task in `CapabilityDetailView` 2. `parseInputs()` extracts `inputs:` from YAML frontmatter 3. If inputs exist: shows input form dialog; if none: runs immediately 4. Opens `TaskRunnerModal` with prompt: `"Read the task instructions at {filePath} and execute them"` (+ input values if any) 5. Modal connects to Pi agent WebSocket via `usePiChat()` 6. Agent reads the `TASK.md`, resolves skill/tool references, executes in the user's sandboxed container 7. `task-logger.ts` (`createTaskLog` -> `appendToLog` -> `finalizeLog`) writes execution log to `DATA_PATH/{email}/logs/tasks/` ## Task Trigger Integration Tasks with `trigger:` in their frontmatter appear in the file browser context menu: - `type: file` + `extensions: [mp3]` -> right-click on `.mp3` files shows this task - `type: directory` -> right-click on directories shows this task Implemented via `useTasks` hook in the `officerdev` workspace, which provides `getMatchingTasks(fileName, entryType)`. ## Seed Sync (Startup) On server startup (`bootstrap.ts`): - `syncSeedSkills()` and `syncSeedTools()` copy/update native seeds to global based on `version:` in frontmatter - `syncSeedTasks()` exists in code but is **not called** in bootstrap ## Legacy Routes Standalone pages still exist using the older `CapabilityPage` component: - `/skills` -> `CapabilityPage` (two-panel, list + detail/chat) - `/tasks` -> `CapabilityPage` - `/processes` -> `CapabilityPage` These share the same API endpoints but use the single-component layout instead of the workspace view.