{"id":31725750,"url":"https://github.com/assistant-ui/mastra-hitl","last_synced_at":"2026-07-11T16:31:30.744Z","repository":{"id":316852425,"uuid":"1064425626","full_name":"assistant-ui/mastra-hitl","owner":"assistant-ui","description":"https://aui-mastra-hitl.vercel.app/","archived":false,"fork":false,"pushed_at":"2026-01-21T04:55:53.000Z","size":683,"stargazers_count":40,"open_issues_count":3,"forks_count":9,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-08T05:12:31.492Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://aui-mastra-hitl.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/assistant-ui.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-26T02:46:37.000Z","updated_at":"2026-02-05T16:38:14.000Z","dependencies_parsed_at":"2025-09-27T06:14:52.758Z","dependency_job_id":"6df9fc21-da4b-4590-8bd8-1257e6bafe6d","html_url":"https://github.com/assistant-ui/mastra-hitl","commit_stats":null,"previous_names":["assistant-ui/mastra-hitl"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/assistant-ui/mastra-hitl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/assistant-ui%2Fmastra-hitl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/assistant-ui%2Fmastra-hitl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/assistant-ui%2Fmastra-hitl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/assistant-ui%2Fmastra-hitl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/assistant-ui","download_url":"https://codeload.github.com/assistant-ui/mastra-hitl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/assistant-ui%2Fmastra-hitl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35368766,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-11T02:00:05.354Z","response_time":104,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-09T05:59:03.537Z","updated_at":"2026-07-11T16:31:30.737Z","avatar_url":"https://github.com/assistant-ui.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Human-in-the-Loop AI Assistant\n\nAn AI agent system with mandatory approval gates at every execution step. Implements a three-phase workflow: planning, approval, and execution with explicit human control points.\n\nBuilt with [assistant-ui](https://github.com/Yonom/assistant-ui) for UI components and [Mastra](https://mastra.dev) for agent orchestration.\n\n## Why assistant-ui?\n\nIf you're building Mastra agents, assistant-ui provides the production-ready UI layer you need. While Mastra handles agent orchestration and tool execution, assistant-ui solves the frontend challenges:\n\n**Streaming UI Components** - Render tool calls and results in real-time as the agent executes. Each tool gets a custom UI component that updates during execution, not after.\n\n**Type-Safe Tool UIs** - Define tool-specific interfaces with `makeAssistantToolUI()`. The approval workflow in this example shows complex multi-step UI (plan editor → approval gate → execution tracker) built with full TypeScript safety.\n\n**Message Threading** - Built-in support for conversation history, message state management, and runtime switching. No need to wire up React state or handle streaming yourself.\n\n**Framework Agnostic** - Works with any LLM provider (Anthropic, OpenAI, etc.) and any agent framework. This example uses Mastra + Claude, but you can swap in different backends without changing UI code.\n\n**What This Example Showcases:**\n- Custom tool UIs for plan approval, todo tracking, and human input collection\n- Real-time streaming updates across multiple tool executions\n- Complex approval workflows with editable plans before execution\n- Integration pattern between Mastra's tool system and React components\n\n## Use Case\n\nStandard AI agents execute actions autonomously, which can lead to unwanted outcomes (sending emails, making API calls, modifying data). This template enforces human oversight by:\n\n1. Requiring plan approval before any action execution\n2. Showing drafts/previews for high-stakes operations (emails, API calls)\n3. Requesting human input when information is missing\n4. Maintaining full execution transparency through real-time todo tracking\n\nSuitable for scenarios where AI assistance is valuable but autonomy is unacceptable: customer communications, data operations, financial transactions.\n\n## Setup\n\n```bash\nnpm install\nnpm run dev\n```\n\nAccess at `http://localhost:3000`\n\nThe `.env` file is pre-configured with your TSAI_API_KEY for accessing the shared Anthropic and Firecrawl proxies.\n\n## Architecture\n\n### Components\n\n```\ncomponents/tools/          # Tool-specific UI implementations\n├── plan-approval.tsx      # Todo list editor with approve/reject\n├── human-in-the-loop.tsx  # Email draft preview \u0026 input forms\n└── todo.tsx               # Real-time todo list display\n\nmastra/\n├── agents/\n│   └── human-in-the-loop-agent.ts  # Agent with approval rules\n└── tools/\n    ├── ask-for-plan-approval-tool.ts   # Plan submission\n    ├── update-todos-tool.ts            # Todo list management\n    ├── request-input-tool.ts           # Human input requests\n    ├── propose-email-tool.ts           # Email draft preview\n    ├── send-email-tool.ts              # Email execution\n    └── firecrawl-tool.ts              # Web scraping\n```\n\n### Workflow\n\n```\nUser Request\n     ↓\nAgent analyzes → Creates todo list → updateTodosTool()\n     ↓\nShows plan UI → askForPlanApprovalTool()\n     ↓\nUser approves/rejects/edits\n     ↓\nIf approved → Execute tasks sequentially\n     ↓\nFor each task:\n  - Need input? → requestInputTool()\n  - Send email? → proposeEmailTool() → (approval) → sendEmailTool()\n  - Web scrape? → firecrawlTool()\n     ↓\nUpdate todos in real-time\n```\n\n## Tools\n\n### 1. Plan Management\n**`updateTodosTool`** - Creates/updates todo list\n- Input: Array of `{content, status, activeForm}`\n- Status: `pending | in_progress | completed`\n- UI updates in real-time via `components/tools/todo.tsx`\n\n**`askForPlanApprovalTool`** - Submits plan for approval\n- Blocks execution until user approves/rejects\n- Returns: `{todos[], approved: boolean}`\n- UI: `components/tools/plan-approval.tsx`\n\n### 2. Human Input\n**`requestInputTool`** - Requests information from user\n- Input: `{label, placeholder}`\n- Blocks until user provides input\n- Returns: `{result: string}`\n- UI: `components/tools/human-in-the-loop.tsx`\n\n### 3. Email Operations\n**`proposeEmailTool`** - Shows email draft for approval\n- Input: `{to, subject, body}`\n- Generates unique handle for tracking\n- Returns: `{emailHandle, approved: boolean}`\n\n**`sendEmailTool`** - Executes approved email\n- Input: `{emailHandle}`\n- Validates handle exists in message history\n- Requires: `RESEND_API_KEY` in environment\n\n### 4. Web Scraping\n**`firecrawlTool`** - Extracts content from websites\n- Input: `{url}`\n- Returns: `{content: markdown}`\n- Rate limited: 20 requests/day per IP (shared proxy)\n- Unlimited with `FIRECRAWL_API_KEY`\n\n## API Access\n\n### Shared Proxies (Pre-configured)\nThe USB deployment includes pre-configured access to rate-limited proxies:\n\n- **Anthropic**: 120 requests/day per IP\n- **Firecrawl**: 20 requests/day per IP\n- **Email**: Requires `RESEND_API_KEY`\n\nAuthentication via `TSAI_API_KEY` (pre-generated on USB).\n\n### Bring Your Own Keys\nFor unlimited access, add to `.env`:\n\n```bash\nANTHROPIC_API_KEY=sk-ant-...  # Direct Anthropic API access\nFIRECRAWL_API_KEY=fc-...      # Direct Firecrawl API access\nRESEND_API_KEY=re-...         # Required for email functionality\n```\n\nWhen API keys are provided, the app bypasses proxies and connects directly to services.\n\n## Agent Configuration\n\nAgent instructions in `mastra/agents/human-in-the-loop-agent.ts`:\n\n```typescript\ninstructions: `\n  MANDATORY WORKFLOW for EVERY request:\n  1. Create a plan using updateTodosTool\n  2. Request approval via ask-for-plan-approval\n  3. Wait for explicit user approval\n  4. Execute only approved tasks\n  5. Update todos to show progress\n\n  KEY RULES:\n  - NEVER act without approval - even for simple tasks\n  - If plan is rejected, revise and request approval again\n  - If new tasks arise during execution, get re-approval\n  - For emails, use propose-email for additional approval\n  - Keep todos current to maintain transparency\n`\n```\n\nThe agent uses Claude Sonnet 4 via either:\n1. Shared proxy (`https://anthropic.tsai.assistant-ui.com/v1`) with `TSAI_API_KEY`\n2. Direct Anthropic API with `ANTHROPIC_API_KEY`\n\n## Extending\n\n### Adding a New Tool\n\n1. **Create tool** (`mastra/tools/your-tool.ts`):\n```typescript\nexport const yourTool = createTool({\n  id: \"your-tool\",\n  description: \"What the tool does\",\n  inputSchema: z.object({...}),\n  execute: async ({ context }) =\u003e {...}\n});\n```\n\n2. **Create UI** (`components/tools/your-tool.tsx`):\n```typescript\nexport const YourToolUI = makeAssistantToolUI({\n  toolName: \"yourTool\",\n  render: ({ args, result, status, addResult }) =\u003e {...}\n});\n```\n\n3. **Register**:\n- Add tool to agent in `mastra/agents/human-in-the-loop-agent.ts`\n- Add UI to `app/assistant.tsx`\n\n### Modifying Approval Logic\n\nEdit `components/tools/plan-approval.tsx` to change:\n- Todo list editing behavior\n- Approval/rejection handling\n- UI presentation\n\n## License\n\nMIT","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fassistant-ui%2Fmastra-hitl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fassistant-ui%2Fmastra-hitl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fassistant-ui%2Fmastra-hitl/lists"}