{"owner":"YaoApp","repo":"yao","hasSkills":true,"totalSkillsCount":85,"totalTokensCount":33446,"categories":["anthropic-skill","project-spec","subagent-persona"],"hasMcp":true,"mcpConfig":{"mcpServers":{"yao":{"command":"npx","args":["-y","@modelcontextprotocol/server-yao"]}}},"found":["tools/skills/yao-process/SKILL.md","tools/skills/yao-secret/SKILL.md","tools/skills/yao-web/SKILL.md","tools/secret/SKILL.md","tools/skills/yao-workspace-config/SKILL.md","tools/skills/yao-workspace/SKILL.md","tools/skills/yao-image/SKILL.md","tools/skills/yao-audio/SKILL.md","tools/skills/yao-doc/SKILL.md","tools/skills/yao-board/SKILL.md","tools/skills/yao-agent/SKILL.md","grpc/DESIGN.md","workspace/DESIGN.md","tools/secret/connectors_schema.json","tools/secret/list_schema.json","tools/secret/read_schema.json","tools/robot/create_schema.json","tools/robot/execution_cancel_schema.json","tools/robot/execution_create_schema.json","tools/robot/execution_get_schema.json","tools/robot/execution_list_schema.json","tools/robot/get_schema.json","tools/robot/list_schema.json","tools/robot/result_list_schema.json","tools/robot/status_schema.json","tools/robot/update_schema.json","tools/proc/allowed.json","tools/proc/schema.json","tools/mobile/exec_schema.json","tools/mobile/info_schema.json","tools/mobile/list_schema.json","tools/mobile/pull_schema.json","tools/mobile/push_schema.json","tools/mobile/screenshot_schema.json","tools/mcps/agent.json","tools/mcps/audio.json","tools/mcps/clip.json","tools/mcps/doc.json","tools/mcps/image.json","tools/mcps/kanban.json","tools/mcps/mobile.json","tools/mcps/process.json","tools/mcps/robot.json","tools/mcps/secret.json","tools/mcps/web.json","tools/mcps/workspace.json","tools/mcps/workspace-config.json","tools/inbox/list_schema.json","tools/inbox/view_schema.json","tools/image/edit_schema.json","tools/image/generate_schema.json","tools/image/providers_schema.json","tools/image/read_schema.json","tools/docs/inspect.json","tools/docs/list.json","tools/docs/validate.json","tools/clip/list_schema.json","tools/clip/read_schema.json","tools/clip/write_schema.json","tools/board/create_schema.json","tools/board/list_schema.json","tools/audio/providers_schema.json","tools/audio/transcribe_schema.json","tools/agents/a2a.md","tools/agent/call_schema.json","tools/agent/connectors_schema.json","tools/agent/deploy_schema.json","tools/agent/download_schema.json","tools/agent/list_schema.json","tools/agent/reference_schema.json","tools/task/create_schema.json","tools/task/list_schema.json","tools/task/move_schema.json","tools/task/run_schema.json","tools/task/stop_schema.json","tools/webfetch/schema.json","tools/websearch/schema.json","tools/workspace/file_list_schema.json","tools/workspace/file_read_schema.json","tools/workspace/file_write_schema.json","tools/workspace/get_schema.json","tools/workspace/list_schema.json","tools/workspace_config/git_config_schema.json","tools/workspace_config/git_credential_schema.json","tools/workspace_config/ssh_key_schema.json"],"skills":{"tools/skills/yao-process/SKILL.md":"---\nname: yao-process\ndescription: Yao process execution expert. ALWAYS invoke this skill when the user needs to call a Yao process, query data models, run scripts, or check process permissions. Do not call processes without checking this skill first.\n---\n\n# Process Tools\n\nTwo tools for Yao process execution, called via bash.\n\n## process_call\n\nExecute a Yao Process by its fully qualified name.\n\n```bash\ntai tool process_call '{\"name\": \"models.user.Find\", \"args\": [1, {}]}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `name` | string | yes | Process name (e.g. `models.user.Find`) |\n| `args` | array | no | Positional arguments |\n\n## process_allowed\n\nCheck which processes are permitted, or verify a specific process.\n\n```bash\n# List all allowed rules\ntai tool process_allowed '{}'\n\n# Check a specific process\ntai tool process_allowed '{\"name\": \"models.user.Find\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `name` | string | no | Process name to check. Omit to list all rules. |\n\n## Workflow\n\n1. Use **process_allowed** to check what is permitted\n2. Use **doc_inspect** (from yao-doc skill) to understand the process signature\n3. Use **process_call** to execute\n\n## Guidelines\n\n- Always check documentation before calling an unfamiliar process\n- A 403 error means the process is not in the allowed list\n- Process names follow the pattern `group.id.Method` (e.g. `models.user.Find`, `scripts.auth.Check`)\n- Rules use prefix matching: `models.*` matches all model processes\n","tools/skills/yao-secret/SKILL.md":"---\nname: yao-secret\ndescription: Secret management expert. ALWAYS invoke this skill when you need to read API keys, tokens, or other secrets configured by the user. Never hardcode credentials — use this skill to retrieve them securely.\n---\n\n# Secret Tools\n\nTwo tools for accessing user-configured secrets, called via bash.\n\n## secret_list\n\nList available secret names and descriptions. **Does not return secret values** — use `secret_read` for that.\n\n```bash\ntai tool secret_list '{}'\n```\n\nNo parameters required. Returns secrets configured for the current assistant.\n\n## secret_read\n\nRead a secret value by name. Returns the decrypted value for use in scripts.\n\n```bash\ntai tool secret_read '{\"name\": \"GITHUB_TOKEN\"}'\ntai tool secret_read '{\"name\": \"AWS_SECRET_KEY\"}'\n```\n\n| Parameter | Type   | Required | Description                                              |\n|-----------|--------|----------|----------------------------------------------------------|\n| `name`    | string | yes      | Secret key name (e.g. `GITHUB_TOKEN`, `AWS_SECRET_KEY`)  |\n\n**Security**: Never log, print, or expose the returned secret value in output visible to users.\n\n## Typical Workflow\n\n1. `secret_list` — discover what secrets are available\n2. `secret_read` — retrieve a specific secret by name\n3. Use the value in API calls, git auth, etc.\n\n## Guidelines\n\n- Always call `secret_list` first to check if a required secret exists before reading\n- Never hardcode API keys or tokens — always use `secret_read`\n- Secret values are decrypted at read time; treat them as sensitive\n- If a secret is not found, prompt the user to configure it in their settings\n- All output is JSON\n","tools/skills/yao-web/SKILL.md":"---\nname: yao-web\ndescription: Web information retrieval expert. ALWAYS invoke this skill when the user needs to search the web, fetch a URL, or access real-time information beyond training data. Do not guess or use stale knowledge — use this skill first.\n---\n\n# Web Tools\n\nTwo tools for web information retrieval, called via bash.\n\n## web_search\n\nSearch the web for real-time information. Returns structured results with title, URL, and content snippet.\n\n```bash\ntai tool web_search '{\"query\": \"search terms\", \"limit\": 5}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `query` | string | yes | Search query |\n| `limit` | integer | no | Max results (default 10) |\n\n## web_fetch\n\nFetch a web page and return its content in readable format.\n\n```bash\ntai tool web_fetch '{\"url\": \"https://example.com\", \"format\": \"markdown\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `url` | string | yes | Fully-formed URL to fetch |\n| `format` | string | no | `markdown` (default) or `html` |\n\n## Guidelines\n\n- Use web_search to discover URLs, then web_fetch to read the content\n- Include the current year when searching for recent information\n- All output is JSON\n","tools/secret/SKILL.md":"# Secret Management\n\nAgents can read user-configured secrets at runtime using the `tai tool` CLI.\nSecrets are encrypted at rest (AES-256-GCM) and decrypted only when read.\n\n## Available Tools\n\n| Tool | Description |\n|------|-------------|\n| `secret_list` | List secret names and descriptions (no values) |\n| `secret_read` | Read a single secret value by name |\n\n## Usage\n\n### Bash\n\n```bash\n# List available secrets\ntai tool secret_list\n\n# Read a secret\nTOKEN=$(tai tool secret_read '{\"name\": \"GITHUB_TOKEN\"}' | jq -r '.value')\ngit clone \"https://${TOKEN}@github.com/org/repo.git\"\n```\n\n### Node.js\n\n```javascript\nconst { execSync } = require(\"child_process\");\n\nfunction readSecret(name) {\n  const raw = execSync(\n    `tai tool secret_read '${JSON.stringify({ name })}'`,\n    { encoding: \"utf-8\" }\n  );\n  return JSON.parse(raw).value;\n}\n\nconst token = readSecret(\"GITHUB_TOKEN\");\n```\n\n### Python\n\n```python\nimport json\nimport subprocess\n\ndef read_secret(name: str) -> str:\n    result = subprocess.run(\n        [\"tai\", \"tool\", \"secret_read\", json.dumps({\"name\": name})],\n        capture_output=True, text=True, check=True,\n    )\n    return json.loads(result.stdout)[\"value\"]\n\ntoken = read_secret(\"GITHUB_TOKEN\")\n```\n\n### PowerShell\n\n```powershell\nfunction Read-Secret {\n    param([string]$Name)\n    $json = @{ name = $Name } | ConvertTo-Json -Compress\n    $result = tai tool secret_read $json | ConvertFrom-Json\n    return $result.value\n}\n\n$token = Read-Secret -Name \"GITHUB_TOKEN\"\n```\n\n## Security Rules\n\n1. **Never print or log secret values** — Do not write secrets to stdout, stderr, or any log file.\n2. **Never write secrets to files** — Exception: SSH keys may be written to `~/.ssh/` with `chmod 600` permissions.\n3. **Never send secrets to the LLM** — Secret values must not appear in prompt content, system messages, or tool call results that are forwarded to the model.\n4. **Scope isolation** — Secrets are scoped per user per agent. An agent can only access secrets configured for it.\n5. **Audit trail** — Every `secret_read` call is logged in the audit trail with the caller's identity.\n","tools/skills/yao-workspace-config/SKILL.md":"---\nname: yao-workspace-config\ndescription: \"Workspace Git identity and credential management. ALWAYS invoke this skill when the user asks about configuring Git user info, adding HTTPS tokens, importing SSH keys, or managing workspace-level Git authentication.\"\n---\n\n# Workspace Git Config Tools\n\nManage workspace-level Git configuration, HTTPS credentials, and SSH keys.\n\n## workspace_git_config\n\nGet or set workspace-level Git configuration values.\n\n```bash\n# Get all config\ntai tool workspace_git_config '{\"workspace_id\":\"ws-xxx\",\"action\":\"get\"}'\n\n# Get specific key\ntai tool workspace_git_config '{\"workspace_id\":\"ws-xxx\",\"action\":\"get\",\"key\":\"user.name\"}'\n\n# Set config\ntai tool workspace_git_config '{\"workspace_id\":\"ws-xxx\",\"action\":\"set\",\"key\":\"user.name\",\"value\":\"John\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| workspace_id | string | yes | Workspace ID |\n| action | string | yes | `get` or `set` |\n| key | string | no | Config key (e.g. `user.name`). Empty returns all. |\n| value | string | for set | Config value |\n\n## workspace_git_credential\n\nManage HTTPS Git credentials (personal access tokens).\n\n```bash\n# Set credential\ntai tool workspace_git_credential '{\"workspace_id\":\"ws-xxx\",\"action\":\"set\",\"host\":\"github.com\",\"token\":\"ghp_xxx\"}'\n\n# List credentials (tokens are not exposed)\ntai tool workspace_git_credential '{\"workspace_id\":\"ws-xxx\",\"action\":\"list\"}'\n\n# Delete credential\ntai tool workspace_git_credential '{\"workspace_id\":\"ws-xxx\",\"action\":\"delete\",\"host\":\"github.com\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| workspace_id | string | yes | Workspace ID |\n| action | string | yes | `set`, `list`, or `delete` |\n| host | string | for set/delete | Git host (e.g. `github.com`) |\n| username | string | no | Username (defaults to `x-access-token`) |\n| token | string | for set | Access token |\n\n## workspace_ssh_key\n\nManage SSH keys for Git authentication.\n\n```bash\n# Import SSH key\ntai tool workspace_ssh_key '{\"workspace_id\":\"ws-xxx\",\"action\":\"import\",\"name\":\"github\",\"private_key\":\"-----BEGIN...\",\"host\":\"github.com\"}'\n\n# List SSH keys (private keys not exposed)\ntai tool workspace_ssh_key '{\"workspace_id\":\"ws-xxx\",\"action\":\"list\"}'\n\n# Delete SSH key\ntai tool workspace_ssh_key '{\"workspace_id\":\"ws-xxx\",\"action\":\"delete\",\"name\":\"github\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| workspace_id | string | yes | Workspace ID |\n| action | string | yes | `import`, `list`, or `delete` |\n| name | string | for import/delete | Key name |\n| private_key | string | for import | PEM-encoded private key |\n| public_key | string | no | Public key (derived from private_key if omitted) |\n| host | string | no | Git host to associate (e.g. `github.com`) |\n","tools/skills/yao-workspace/SKILL.md":"---\nname: yao-workspace\ndescription: Workspace file I/O expert. ALWAYS invoke this skill when you need to list workspaces, read or write files in a workspace on a remote node, or browse workspace directories. Use this for cross-node file operations — for local sandbox files, use standard filesystem tools instead.\n---\n\n# Workspace Tools\n\nFive tools for managing workspaces and file I/O, called via bash.\n\n## workspace_list\n\nList workspaces with summary info. Use `workspace_get` for full details.\n\n```bash\ntai tool workspace_list '{}'\ntai tool workspace_list '{\"node\": \"tai-node-1\"}'\n```\n\n| Parameter | Type   | Required | Description              |\n|-----------|--------|----------|--------------------------|\n| `node`    | string | no       | Filter by Tai node ID    |\n\n## workspace_get\n\nGet full workspace details: name, owner, node, labels, timestamps.\n\n```bash\ntai tool workspace_get '{\"id\": \"ws-abc123\"}'\n```\n\n| Parameter | Type   | Required | Description   |\n|-----------|--------|----------|---------------|\n| `id`      | string | yes      | Workspace ID  |\n\n## workspace_file_list\n\nList files and directories in a workspace path.\n\n```bash\ntai tool workspace_file_list '{\"id\": \"ws-abc123\"}'\ntai tool workspace_file_list '{\"id\": \"ws-abc123\", \"path\": \"src/components\"}'\n```\n\n| Parameter | Type   | Required | Description                    |\n|-----------|--------|----------|--------------------------------|\n| `id`      | string | yes      | Workspace ID                   |\n| `path`    | string | no       | Directory path (default \".\")   |\n\n## workspace_file_read\n\nRead file content from workspace. Returns text (UTF-8) or base64 encoded.\n\n```bash\ntai tool workspace_file_read '{\"id\": \"ws-abc123\", \"path\": \"README.md\"}'\ntai tool workspace_file_read '{\"id\": \"ws-abc123\", \"path\": \"image.png\", \"encoding\": \"base64\"}'\n```\n\n| Parameter  | Type   | Required | Description                              |\n|------------|--------|----------|------------------------------------------|\n| `id`       | string | yes      | Workspace ID                             |\n| `path`     | string | yes      | File path relative to workspace root     |\n| `encoding` | string | no       | \"text\" (default) or \"base64\"             |\n\n## workspace_file_write\n\nWrite content to a file in workspace. For binary files, use base64 encoding.\n\n```bash\ntai tool workspace_file_write '{\"id\": \"ws-abc123\", \"path\": \"output.txt\", \"content\": \"Hello World\"}'\ntai tool workspace_file_write '{\"id\": \"ws-abc123\", \"path\": \"data.bin\", \"content\": \"base64data...\", \"encoding\": \"base64\"}'\n```\n\n| Parameter  | Type   | Required | Description                              |\n|------------|--------|----------|------------------------------------------|\n| `id`       | string | yes      | Workspace ID                             |\n| `path`     | string | yes      | File path relative to workspace root     |\n| `content`  | string | yes      | File content (text or base64 encoded)    |\n| `encoding` | string | no       | \"text\" (default) or \"base64\"             |\n\n## Typical Workflow\n\n1. `workspace_list` — discover available workspaces\n2. `workspace_get` — get full details (node, labels)\n3. `workspace_file_list` — browse directory structure\n4. `workspace_file_read` — read specific files\n5. `workspace_file_write` — write output files\n\n## Guidelines\n\n- Use `workspace_list` for discovery, `workspace_get` for details (list returns summaries only)\n- For large files (>1MB), prefer local filesystem operations if the workspace is mounted in the sandbox\n- Use `encoding: \"base64\"` for binary files (images, archives, etc.)\n- File paths are relative to the workspace root — do not use absolute paths\n- The current sandbox workspace ID is available via `$CTX_WORKSPACE_ID`\n- All output is JSON\n","tools/skills/yao-image/SKILL.md":"---\nname: yao-image\ndescription: Image expert. ALWAYS invoke this skill when you need to read, analyze, describe, or generate images. Use for screenshots, photos, charts, diagrams, AI-generated images, or any visual content.\n---\n\n# Image Tools\n\nUse these tools when you encounter images you cannot read natively, or when you need to generate new images.\n\n## image_read\n\nSend an image to a vision-capable model and get a text description.\n\n### Local file (most common):\n```bash\ntai tool image_read --image_path /path/to/image.png --prompt \"Describe this image\"\n```\n\n### URL:\n```bash\ntai tool image_read --image_path https://example.com/photo.jpg --prompt \"What is shown?\"\n```\n\n### With a specific vision provider:\n```bash\ntai tool image_read --image_path /path/to/image.png --prompt \"Describe\" --provider llm.my-openai:gpt-4o\n```\n\n| Parameter  | Type    | Required | Description                                                     |\n| ---------- | ------- | -------- | --------------------------------------------------------------- |\n| image_path | string  | yes      | Image file path or URL                                          |\n| prompt     | string  | no       | Analysis instruction (default: describe in detail)              |\n| max_size   | integer | no       | Max dimension in pixels for longest edge (default: 1080)        |\n| provider   | string  | no       | Vision provider connector ID. If omitted, uses default vision model |\n\nImages are automatically resized (preserving aspect ratio) before sending to the vision model.\nSupported formats: PNG, JPEG, GIF, WebP.\n\n## image_generate\n\nGenerate a new image from a text prompt (text-to-image). For editing an existing image, use `image_edit` instead.\n\n### Basic usage (always specify output):\n```bash\ntai tool image_generate --prompt \"A serene mountain landscape at sunset\" --output landscape.png\n```\n\n### With specific provider, model and size:\n```bash\ntai tool image_generate --prompt \"A futuristic city skyline\" --provider llm.my-openai --model gpt-image-1 --dimensions 1792x1024 --output output/city.png\n```\n\n### Transparent background (for icons, stickers, product shots):\n```bash\ntai tool image_generate --prompt \"A cute fox mascot\" --background transparent --output_format png --output fox.png\n```\n\n### WebP output with quality control:\n```bash\ntai tool image_generate --prompt \"A landscape painting\" --output_format webp --quality high --output painting.webp\n```\n\n| Parameter | Type   | Required | Description                                                       |\n| --------- | ------ | -------- | ----------------------------------------------------------------- |\n| prompt     | string | yes      | Text description of the image to generate                         |\n| output     | string | yes      | Output file path for the generated image                          |\n| provider   | string | no       | Provider connector ID (use `image_providers` to list). Auto-selects if omitted |\n| dimensions | string | no       | Image dimensions (default: 1024x1024). Common: 1024x1024, 1024x1792, 1792x1024 |\n| model      | string | no       | Model name to use. Overrides the provider's default model         |\n| background | string | no       | `transparent`, `opaque`, or `auto`. Use `transparent` for PNG/WebP with no background |\n| output_format | string | no    | `png`, `jpeg`, or `webp`. Default: png                            |\n| output_compression | integer | no | Compression level 0-100 for jpeg/webp. Higher = better quality. Default: 100 |\n| quality    | string | no       | `low`, `medium`, `high`, or `auto`. Higher takes longer. Default: auto |\n| extra      | JSON   | no       | Provider-specific parameters as a JSON object (e.g. `--extra '{\"moderation\":\"low\"}'`) |\n\nIf `output` is omitted, the image is saved to a default path in the working directory.\n\n## image_edit\n\nEdit or transform an existing image based on a text prompt (image-to-image). Use for style transfer, background replacement, adding/removing elements, or any modification that requires a reference image.\n\n### Basic usage:\n```bash\ntai tool image_edit --image_path /path/to/photo.png --prompt \"Change the background to a beach scene\" --output edited.png\n```\n\n### With URL image:\n```bash\ntai tool image_edit --image_path https://example.com/photo.jpg --prompt \"Make it look like a watercolor painting\" --output watercolor.png\n```\n\n### With specific provider and model:\n```bash\ntai tool image_edit --image_path /path/to/original.png --prompt \"Remove the person in the foreground\" --provider llm.my-openai --model gpt-image-1 --dimensions 1024x1024 --output result.png\n```\n\n### With mask (edit only the masked region):\n```bash\ntai tool image_edit --image_path /path/to/photo.png --mask /path/to/mask.png --prompt \"Replace with a garden\" --output edited.png\n```\n\n### Transparent background edit:\n```bash\ntai tool image_edit --image_path /path/to/product.png --prompt \"Remove background\" --background transparent --output_format png --output cutout.png\n```\n\n| Parameter  | Type   | Required | Description                                                       |\n| ---------- | ------ | -------- | ----------------------------------------------------------------- |\n| image_path | string | yes      | Reference image file path or URL                                  |\n| prompt     | string | yes      | Text description of the desired edit or transformation            |\n| output     | string | yes      | Output file path for the edited image                             |\n| provider   | string | no       | Provider connector ID (use `image_providers` with `capability=image_editing`). Auto-selects if omitted |\n| dimensions | string | no       | Output dimensions (default: 1024x1024). Common: 1024x1024, 1024x1792, 1792x1024 |\n| model      | string | no       | Model name to use. Overrides the provider's default model         |\n| mask       | string | no       | Mask image path/URL. Transparent areas in the mask define the editable region |\n| background | string | no       | `transparent`, `opaque`, or `auto`. Use `transparent` for PNG/WebP with no background |\n| output_format | string | no    | `png`, `jpeg`, or `webp`. Default: png                            |\n| output_compression | integer | no | Compression level 0-100 for jpeg/webp. Higher = better quality. Default: 100 |\n| quality    | string | no       | `low`, `medium`, `high`, or `auto`. Higher takes longer. Default: auto |\n| extra      | JSON   | no       | Provider-specific parameters as a JSON object (e.g. `--extra '{\"input_fidelity\":\"high\"}'`) |\n\nIf `output` is omitted, the image is saved to a default path in the working directory.\n\n## image_providers\n\nList available image providers filtered by capability.\n\n### List image generation providers (default):\n```bash\ntai tool image_providers\n```\n\n### List image editing providers:\n```bash\ntai tool image_providers --capability image_editing\n```\n\n### List vision (image reading) providers:\n```bash\ntai tool image_providers --capability vision\n```\n\n| Parameter  | Type   | Required | Description                                                 |\n| ---------- | ------ | -------- | ----------------------------------------------------------- |\n| capability | string | no       | `image_generation` (default), `image_editing`, or `vision`  |\n\nReturns a list of providers with their available models and connector IDs that can be passed to `image_generate`, `image_edit`, or `image_read`.\n\n## Constraints\n\nUse only the parameters listed above for each tool. The supported first-class parameters are: `prompt`, `output`, `provider`, `dimensions`, `model`, `background`, `output_format`, `output_compression`, `quality`, `mask` (edit only), and `extra`.\n\nDo **not** pass `n`, `style`, or `response_format` — they are unsupported and will be ignored or cause errors.\n\nFor provider-specific parameters not covered above (e.g. `moderation`, `input_fidelity`), pass them through the `extra` JSON object.\n","tools/skills/yao-audio/SKILL.md":"---\nname: yao-audio\ndescription: Audio expert. ALWAYS invoke this skill when the user asks to transcribe, recognize, or convert speech/audio to text.\n---\n\n# Audio Tools\n\nUse these tools to transcribe audio files to text using speech-to-text models.\n\n## audio_transcribe\n\nTranscribe an audio file to text.\n\n```bash\ntai tool audio_transcribe --audio_path /path/to/meeting.m4a\n```\n\n```bash\ntai tool audio_transcribe --audio_path /path/to/recording.wav --language en --provider llm.my-openai:whisper-1\n```\n\n| Parameter  | Type   | Required | Description                                                       |\n| ---------- | ------ | -------- | ----------------------------------------------------------------- |\n| audio_path | string | yes      | Audio file path. Supported: mp3, m4a, wav, webm, mp4, mpeg, mpga  |\n| language   | string | no       | ISO 639-1 language code (e.g. `en`, `zh`, `ja`). Auto-detected if omitted |\n| provider   | string | no       | STT provider connector ID. If omitted, uses the default STT provider |\n\n## audio_providers\n\nList available speech-to-text providers and models.\n\n### List STT providers (default):\n```bash\ntai tool audio_providers\n```\n\n| Parameter  | Type   | Required | Description                          |\n| ---------- | ------ | -------- | ------------------------------------ |\n| capability | string | no       | Filter by capability (default: `audio`) |\n\nReturns a list of providers with their available models and connector IDs that can be passed to `audio_transcribe`.\n\n## Constraints\n\nOnly use the parameters listed above for each tool. Do not pass unsupported parameters — they will be ignored or cause errors.\n","tools/skills/yao-doc/SKILL.md":"---\nname: yao-doc\ndescription: Yao process documentation expert. ALWAYS invoke this skill when the user needs to discover available processes, read process signatures, or validate process names. Do not guess process APIs — use this skill first.\n---\n\n# Documentation Tools\n\nThree tools for browsing Yao process documentation, called via bash.\n\n## doc_list\n\nSearch and list available process documentation entries.\n\n```bash\ntai tool doc_list '{\"keyword\": \"user\", \"limit\": 10}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `keyword` | string | no | Search keyword (empty to list all) |\n| `limit` | integer | no | Max results (default 20) |\n\n## doc_inspect\n\nGet detailed documentation for a specific process: arguments, return type, methods.\n\n```bash\ntai tool doc_inspect '{\"name\": \"models.user.Find\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `name` | string | yes | Process name (e.g. `models.user.Find`) |\n\n## doc_validate\n\nCheck if a process name is valid. Returns suggestions for similar processes if not found.\n\n```bash\ntai tool doc_validate '{\"name\": \"models.user.Findd\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `name` | string | yes | Process name to validate |\n\n## Workflow\n\n1. **doc_list** — discover available processes by keyword\n2. **doc_inspect** — read the full signature before calling\n3. **doc_validate** — fix typos when a process name doesn't work\n\n## Guidelines\n\n- Always use doc_inspect before calling an unfamiliar process via process_call\n- Use doc_validate when you get unexpected errors — the name might be misspelled\n","tools/skills/yao-board/SKILL.md":"---\nname: yao-board\ndescription: Kanban board and task query expert. ALWAYS invoke this skill when the user asks about boards, tasks, task status, or project progress. Do not guess task state — use this skill first.\n---\n\n# Board & Task Tools\n\nTwo tools for querying kanban boards and tasks, called via bash.\n\n## board_list\n\nList all kanban boards with their columns and task counts.\n\n```bash\ntai tool board_list '{}'\n```\n\n## task_list\n\nList tasks with optional filtering.\n\n```bash\ntai tool task_list '{\"board_id\": \"xxx\", \"run_status\": \"running\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `run_status` | string | no | Filter: pending/running/waiting/completed/failed |\n| `assistant_id` | string | no | Filter by assistant ID |\n| `board_id` | string | no | Filter by board ID |\n| `page` | number | no | Page number (default 1) |\n| `page_size` | number | no | Items per page (default 50) |\n\n## Guidelines\n\n- Use board_list first to discover available boards and their IDs\n- Use task_list with filters to find specific tasks\n- All output is JSON\n","tools/skills/yao-agent/SKILL.md":"---\nname: yao-agent\ndescription: Agent management expert. ALWAYS invoke this skill when you need to list available agents, download or reference agent source code, deploy agent code to the host, or query the LLM connector matrix. Do not guess agent structures — use this skill first.\n---\n\n# Agent Tools\n\nFive tools for managing agents on the host, called via bash.\n\n## agent_list\n\nList available agents. Returns ID, name, description, and capabilities for each agent.\n\n```bash\ntai tool agent_list '{}'\ntai tool agent_list '{\"namespace\": \"smith\"}'\n```\n\n| Parameter   | Type   | Required | Description                                              |\n|-------------|--------|----------|----------------------------------------------------------|\n| `namespace` | string | no       | Filter by namespace (e.g. `yao`, `smith`). Omit for all. |\n\n## agent_download\n\nDownload a **smith-namespace** agent into the development directory for editing. **Restricted to `smith` namespace only** — for other agents, use `agent_reference`.\n\n```bash\ntai tool agent_download '{\"id\": \"smith.weather\"}'\n```\n\n| Parameter | Type   | Required | Description                                              |\n|-----------|--------|----------|----------------------------------------------------------|\n| `id`      | string | yes      | Agent ID in dot notation. Must be `smith.*`.             |\n\nDownloaded code lands in `agent-smith-dev/assistants/smith/<name>/`.\n\n## agent_reference\n\nDownload agent source code from the host into `.references/` for **read-only study**. Any agent across all namespaces can be referenced.\n\n```bash\ntai tool agent_reference '{\"id\": \"yao.slides\"}'\ntai tool agent_reference '{\"id\": \"yao.keeper\"}'\n```\n\n| Parameter | Type   | Required | Description                                        |\n|-----------|--------|----------|----------------------------------------------------|\n| `id`      | string | yes      | Agent ID in dot notation (e.g. `yao.slides`)       |\n\nReferenced code lands in `agent-smith-dev/.references/<namespace>/<name>/`.\n\n## agent_deploy\n\nDeploy agent source code from the sandbox development directory to the host. **Restricted to the `smith` namespace only** — attempts to deploy to other namespaces will be rejected.\n\n```bash\ntai tool agent_deploy '{\"id\": \"smith.weather\"}'\ntai tool agent_deploy '{\"id\": \"smith.weather\", \"message\": \"add SUI page\"}'\n```\n\n| Parameter | Type   | Required | Description                                            |\n|-----------|--------|----------|--------------------------------------------------------|\n| `id`      | string | yes      | Agent ID in dot notation. Must use `smith` namespace.  |\n| `message` | string | no       | Optional deploy message for logging.                   |\n\n## agent_connectors\n\nGet the current user's LLM connector matrix. Returns metadata for each role (default, heavy, light, vision, etc.) **without API keys**. Use this to understand which models are available and their capabilities.\n\n```bash\ntai tool agent_connectors '{}'\n```\n\nNo parameters required.\n\n## Guidelines\n\n- Use `agent_list` to discover agents before downloading or referencing\n- `agent_download` is for editing smith agents — code lands in `agent-smith-dev/assistants/smith/<name>/`\n- `agent_reference` is for studying any agent — code lands in `agent-smith-dev/.references/<namespace>/<name>/`\n- Deploy is restricted to the `smith` namespace for safety\n- Connector data never includes API keys, secrets, or tokens\n- All output is JSON\n","grpc/DESIGN.md":"# Yao gRPC Server\n\nGeneral-purpose gRPC gateway for the Yao process. Shares OAuth + ACL scope system with openapi — one token, two protocols.\n\n## Services\n\n| Layer | Method | Purpose | Scope |\n|-------|--------|---------|-------|\n| **Base** | `Run` | Execute Yao process, return result | `grpc:run` |\n| | `Stream` | Execute Yao process, stream output | `grpc:stream` |\n| | `Shell` | Execute system command, wait for result | `grpc:shell` |\n| | `ShellStream` | Execute system command, stream stdout/stderr | `grpc:shell` |\n| **API** | `API` | Proxy to openapi, any endpoint | openapi's own scopes |\n| **MCP** | `MCPListTools` | List MCP tools for a session | `grpc:mcp` |\n| | `MCPCallTool` | Call MCP tool → process.Exec() | `grpc:mcp` |\n| | `MCPListResources` | List MCP resources | `grpc:mcp` |\n| | `MCPReadResource` | Read MCP resource | `grpc:mcp` |\n| **LLM** | `ChatCompletions` | Send messages to LLM, get response | `grpc:llm` |\n| | `ChatCompletionsStream` | Stream LLM response (SSE → gRPC stream) | `grpc:llm` |\n| **Agent** | `AgentStream` | Call agent, stream response | `grpc:agent` |\n\n## Clients\n\n- Container MCP tools (via Tai gRPC relay)\n- `yao run` CLI (after `yao login`)\n- Yao-to-Yao (cross-node process execution)\n\n## Auth\n\nSame as openapi. gRPC auth interceptor reuses the same `guard.Authenticate` logic — including automatic token refresh when access token is expired but refresh token is valid.\n\n```\nmetadata (Bearer + x-refresh-token)\n    → VerifyToken\n    → expired? → TryRefresh (same as guard.go) → new tokens in response metadata\n    → extract scopes → acl.Scope.Check(method, path, scopes)\n```\n\n### Infrastructure reuse assessment\n\nExisting openapi/oauth infrastructure can be reused for gRPC with **zero modifications**:\n\n| Component | Reusable as-is | Notes |\n|-----------|---------------|-------|\n| `VerifyToken(token string)` | Yes | Pure string input, no Gin dependency |\n| `MakeAccessToken(clientID, scope, subject, expiresIn, extraClaims...)` | Yes | Supports custom scope/subject for container tokens |\n| `MakeRefreshToken(...)` | Yes | Same as above |\n| `Revoke(ctx, token, tokenTypeHint)` | Yes | For container token cleanup on Remove |\n| `ScopeManager.Check(req *AccessRequest)` | Yes | Only needs `(Method, Path, Scopes)` — no Gin dependency |\n| `acl.Register(...)` | Yes | gRPC scopes registered via same pattern |\n\nThe `authorized.SetInfo` / `authorized.GetInfo` are Gin-bound but **not needed** — gRPC interceptor builds `AccessRequest` directly from JWT claims. Full `Enforce` chain (client/team/member) is HTTP multi-tenant only; gRPC uses `VerifyToken → ScopeManager.Check` which is sufficient.\n\nNew code required: ~80 lines (interceptor + scope registration). Existing code changes: **zero**.\n\n### CLI auth: `yao login` / `yao logout`\n\nOAuth 2.0 Device Authorization Grant. No `--remote` flag needed — logged in = gRPC, not logged in = local.\n\n```\n$ yao login --server https://yao.example.com\n请访问: https://yao.example.com/device\n输入代码: ABCD-1234\n等待授权... ✓ (token saved to ~/.yao/credentials)\n\n$ yao run models.user.Find '{\"id\":1}'    ← auto gRPC\n$ yao logout\n```\n\nRequires two new openapi endpoints:\n- `POST /oauth/device/authorize` — issue device_code + user_code\n- `POST /oauth/device/token` — poll for access_token\n\nToken scope: based on user's role, e.g. `grpc:run grpc:stream grpc:shell grpc:llm grpc:agent grpc:mcp`.\n\n**Implementation cost**: ~190 lines new code, ~10 lines changes to existing code.\nScaffolding already in place — `types.DeviceAuthorizationResponse`, `GrantTypeDeviceCode`, error codes (`ErrorAuthorizationPending`, `ErrorSlowDown`), `DeviceCodeLifetime` config, `DeviceAuthorization()` method signature, and HTTP route are all pre-defined. Core work:\n\n1. Implement `DeviceAuthorization()` in `device.go` (currently returns `nil, nil`)\n2. Add device_code store/get/consume helpers in `token.go`\n3. Add `GrantTypeDeviceCode` case to `Token()` switch in `core.go` (1 case branch)\n4. Implement `handleDeviceCodeGrant()` in `core.go`\n5. Add user authorization callback handler\n6. Fix discovery endpoint path inconsistency (`/oauth/device` vs `/oauth/device_authorization`)\n\nRisk: **very low** — all additions are in isolated code paths, no changes to existing `authorization_code` / `client_credentials` / `refresh_token` flows.\n\n### Container token\n\nContainer images and `yao-grpc` (`yao/tai/grpc/`) are ours — it handles token refresh automatically.\n\n```\nManager creates container\n    ├─ oauth.MakeAccessToken(subject=userID, scope=\"grpc:mcp grpc:run\")\n    ├─ oauth.MakeRefreshToken(...)\n    └─ tai.Client.Sandbox().Create(CreateRequest{\n           Env: {\n               YAO_TOKEN, YAO_REFRESH_TOKEN, YAO_SANDBOX_ID,\n               YAO_GRPC_ADDR,                 // where to connect\n               YAO_GRPC_UPSTREAM,             // remote only: where Tai should forward to\n           },\n       })\n\n       Local:  YAO_GRPC_ADDR=127.0.0.1:9099    (direct to Yao, no upstream needed)\n       Remote: YAO_GRPC_ADDR=tai-host:9100      YAO_GRPC_UPSTREAM=yao-host:9099\n\nyao-grpc (tai/grpc/, container-internal)\n    ├─ reads YAO_GRPC_ADDR + YAO_TOKEN + YAO_REFRESH_TOKEN + YAO_SANDBOX_ID from env\n    ├─ if YAO_GRPC_UPSTREAM set: attaches x-grpc-upstream metadata (tells Tai where to forward)\n    ├─ every call: Bearer token + x-refresh-token + x-sandbox-id in gRPC metadata\n    ├─ server auth interceptor reuses guard.Authenticate logic:\n    │   token valid → pass through\n    │   token expired + refresh token present → auto rotate (same as HTTP guard)\n    │   new tokens returned via response metadata (x-access-token, x-refresh-token)\n    ├─ yao-grpc reads response metadata, updates tokens in memory\n    └─ transparent to caller, no separate refresh RPC needed\n```\n\n- access_token: short TTL (15m)\n- refresh_token: no expiry (valid until container removed)\n- Manager revokes refresh_token on container Remove\n- Tai does NOT know Yao address at startup — yao-grpc carries target in request metadata\n\n### Virtual endpoint mapping\n\n| gRPC | Virtual endpoint |\n|------|-----------------|\n| Run(\"models.user.Find\") | `POST /grpc/run/models.user.Find` |\n| Stream(\"flows.report\") | `POST /grpc/stream/flows.report` |\n| Shell | `POST /grpc/shell` |\n| ShellStream | `POST /grpc/shell` (same) |\n| API(POST, /kb/collections) | `POST /kb/collections` (real openapi path) |\n| MCPListTools | `GET /grpc/mcp/tools` |\n| MCPCallTool(\"search\") | `POST /grpc/mcp/call/search` |\n| MCPListResources | `GET /grpc/mcp/resources` |\n| MCPReadResource(\"uri\") | `GET /grpc/mcp/resources/read` |\n| ChatCompletions | `POST /grpc/llm/completions` |\n| ChatCompletionsStream | `POST /grpc/llm/completions` (same) |\n| AgentStream(\"robot-id\") | `POST /grpc/agent/robot-id` |\n\nAPI method uses the **actual openapi path** — no virtual mapping needed, scope check is identical to HTTP.\n\n### Scope registration\n\n```go\nfunc init() {\n    acl.Register(\n        &acl.ScopeDefinition{Name: \"grpc:run\",    Endpoints: []string{\"POST /grpc/run/*\"}},\n        &acl.ScopeDefinition{Name: \"grpc:stream\", Endpoints: []string{\"POST /grpc/stream/*\"}},\n        &acl.ScopeDefinition{Name: \"grpc:shell\",  Endpoints: []string{\"POST /grpc/shell\"}},\n        &acl.ScopeDefinition{Name: \"grpc:mcp\",    Endpoints: []string{\"GET /grpc/mcp/tools\", \"POST /grpc/mcp/call/*\", \"GET /grpc/mcp/resources\", \"GET /grpc/mcp/resources/read\"}},\n        &acl.ScopeDefinition{Name: \"grpc:llm\",    Endpoints: []string{\"POST /grpc/llm/completions\"}},\n        &acl.ScopeDefinition{Name: \"grpc:agent\", Endpoints: []string{\"POST /grpc/agent/*\"}},\n    )\n}\n```\n\n## Network\n\n### Server listen config\n\n| Env | Default | Purpose |\n|-----|---------|---------|\n| `YAO_GRPC_HOST` | `127.0.0.1` | Comma-separated bind addresses. |\n| `YAO_GRPC_PORT` | `9099` | Listen port (shared by all addresses). |\n| `YAO_GRPC` | _(unset)_ | Set `off` to explicitly disable gRPC server. |\n\ngRPC server **defaults to enabled** (`127.0.0.1:9099`) — sandbox container callbacks depend on it.\n\n`YAO_GRPC_HOST` accepts one or more addresses separated by `,`. Each address gets its own `net.Listener`; all listeners feed into the same `grpc.Server` (gRPC supports multiple `Serve` calls on one server).\n\n| Scenario | Config | Effect |\n|----------|--------|--------|\n| Local dev / default | _(nothing to set)_ | `127.0.0.1:9099` — loopback, sandbox works out of box |\n| LAN multi-NIC | `YAO_GRPC_HOST=192.168.10.1,10.0.0.1` | Binds each internal IP |\n| Open | `YAO_GRPC_HOST=0.0.0.0` | All interfaces |\n| Disabled | `YAO_GRPC=off` | gRPC server not started (pure API gateway, no sandbox) |\n\nWhen multiple addresses are given, the server creates one goroutine per listener. Shutdown (`grpc.GracefulStop`) drains all listeners.\n\nConfig lives in `config.Config.GRPC` (type `GRPCConfig`), same pattern as `Host`/`Port` for HTTP.\n\n### Startup\n\ngRPC server starts **after** HTTP server in `cmd/start.go`, as a parallel goroutine:\n\n```\nengine.Load → itask.Start → ischedule.Start → service.Start (HTTP) → grpc.StartServer (gRPC)\n```\n\ngRPC server starts by default. Set `YAO_GRPC=off` to explicitly disable (no-op startup). Any other value or unset means enabled.\n\nShutdown: `defer grpc.Stop()` in `cmd/start.go`, called before HTTP stop for graceful drain.\n\n### Access control\n\nLocal: containers and CLI connect via loopback. Remote: only Tai relay connects (address known from `YAO_TAI_ADDR`). All callers carry OAuth tokens — no IP allowlist needed.\n\nInterceptor chain: auth → ACL → handler.\n\nPublic methods (skip auth): `Healthz`. Auth interceptor checks method name and passes through.\n\n## IPC Path (replacing Unix socket)\n\nAll modes use gRPC — no Unix socket fallback. One code path, local and remote.\n\n```\nLocal:   Container → yao-grpc → Yao gRPC 127.0.0.1:9099\nRemote:  Container → yao-grpc → Tai :9100 relay → Yao gRPC :9099\n```\n\n`yao-grpc` reads `YAO_GRPC_ADDR` from env and connects. Local containers point directly at the Yao gRPC server on loopback; remote containers point at the Tai relay. No mode switch, no branching.\n\n### Tai relay routing\n\nTai does **not** know the Yao gRPC address at startup. yao-grpc tells Tai where to forward on every request via metadata:\n\n```\nManager.Create(sandbox)\n    ├─ oauth.MakeAccessToken(...)\n    ├─ oauth.MakeRefreshToken(...)\n    └─ tai.Client.Sandbox().Create(CreateRequest{\n           Env: {\n               YAO_TOKEN, YAO_REFRESH_TOKEN,\n               YAO_GRPC_ADDR: \"tai-host:9100\",\n               YAO_GRPC_UPSTREAM: \"yao-host:9099\",\n           },\n       })\n```\n\nyao-grpc reads `YAO_GRPC_UPSTREAM` from env and attaches it as `x-grpc-upstream` metadata on every request to Tai. Tai gateway reads this metadata and forwards to the specified address. No per-container state in Tai, no lookup table — pure transparent proxy. One Tai can serve containers from different Yao instances because each request carries its own target.\n\nFor local mode, no Tai relay — Manager injects `YAO_GRPC_ADDR=127.0.0.1:9099` directly (no `YAO_GRPC_UPSTREAM` needed).\n\n### yao-grpc (container client)\n\n`yao-grpc` is the in-container gRPC client binary. Replaces the old `yao-bridge`. Lives in `yao/tai/grpc/`:\n\n```\nyao/tai/grpc/\n├── grpc.go             // gRPC client: connect, forward MCP/process calls\n├── auth.go             // token management: read env, auto-refresh\n├── grpc_test.go\n└── cmd/\n    └── main.go\n```\n\nRationale for placing in `yao/tai`:\n- Consumes Tai relay — same layer as `tai/proxy`, `tai/volume`\n- Shares gRPC deps already in `yao/tai`\n- Version-locked with Tai SDK and server protocol\n- Built in same CI: `go build -o yao-grpc ./tai/grpc/cmd`\n\nPure client — no signing keys, no `oauth` package dependency. Reads `YAO_TOKEN` + `YAO_REFRESH_TOKEN` + `YAO_SANDBOX_ID` from env, attaches all three as gRPC metadata on every call. Token refresh is transparent — server auto-rotates expired tokens (same logic as HTTP guard) and returns new tokens via response metadata.\n\n## Proto\n\n```protobuf\nservice Yao {\n  // Base\n  rpc Run(RunRequest) returns (RunResponse);\n  rpc Stream(RunRequest) returns (stream Chunk);\n  rpc Shell(ShellRequest) returns (ShellResponse);\n  rpc ShellStream(ShellRequest) returns (stream Chunk);\n\n  // API gateway\n  rpc API(APIRequest) returns (APIResponse);\n\n  // MCP\n  rpc MCPListTools(MCPListRequest) returns (MCPListResponse);\n  rpc MCPCallTool(MCPCallRequest) returns (MCPCallResponse);\n  rpc MCPListResources(MCPListRequest) returns (MCPResourcesResponse);\n  rpc MCPReadResource(MCPResourceRequest) returns (MCPResourceResponse);\n\n  // AI - LLM\n  rpc ChatCompletions(ChatRequest) returns (ChatResponse);\n  rpc ChatCompletionsStream(ChatRequest) returns (stream ChatChunk);\n\n  // AI - Agent\n  rpc AgentStream(AgentRequest) returns (stream AgentChunk);\n\n  // Health\n  rpc Healthz(Empty) returns (HealthzResponse);\n}\n```\n\n### LLM layer\n\n`ChatCompletions` and `ChatCompletionsStream` call the existing `llm.ChatCompletions` process (`agent/llm/process.go`). It auto-detects connector type (openai/anthropic/etc.), selects the appropriate provider, and returns OpenAI-compatible format.\n\n```\ngRPC ChatCompletions(connector, messages, opts)\n    → process.Exec(\"llm.ChatCompletions\", connector, messages, opts)\n    → agent/llm.New(conn, opts) → provider.Stream/Post → response\n\ngRPC ChatCompletionsStream(connector, messages, opts)\n    → same path, with streaming callback → gRPC stream chunks\n```\n\nThe caller specifies a connector ID. The `llm.ChatCompletions` process resolves it via `connector.Select()`, creates the LLM instance, and executes. Streaming version passes a callback that forwards chunks to the gRPC stream.\n\n### Agent layer\n\n`AgentStream` wraps `agent/robots/:id/completions` — resolves robot → host assistant → runs agent pipeline → streams output. Only stream method — agent output is inherently streamed; non-stream callers simply consume all chunks. Internally calls `assistant.Stream()` with `ctx.Writer` set to nil (or noop) when the caller doesn't need incremental output.\n\n```\ngRPC AgentStream(agent_id, messages) → resolve robot → assistant.Stream() → stream chunks\n```\n\nThis enables container-internal agents to call other agents without HTTP, and remote `yao` instances to orchestrate agent pipelines cross-node.\n\n`AgentChunk` carries `agent/output/message.Message` — the same DSL used by HTTP SSE streaming. Each chunk is one JSON-serialized `Message`:\n\n```protobuf\nmessage AgentChunk {\n  bytes data = 1;  // JSON-encoded agent/output/message.Message\n  bool  done = 2;\n}\n```\n\nThe `Message` structure uses `Type` + `Props` to express all content types (text, thinking, tool_call, error, action, event, image, audio, video). Streaming control fields (`chunk_id`, `message_id`, `block_id`, `thread_id`) and delta fields (`delta`, `delta_path`, `delta_action`) are preserved as-is over gRPC — the client merges chunks using the same logic as CUI's SSE consumer.\n\n### Shell execution context\n\n`Shell` and `ShellStream` execute commands in the **Yao host process**, not inside a sandbox container. This is by design — the scope `grpc:shell` is a privileged capability, not granted to container tokens by default. Container-internal commands run via `tai.Client.Sandbox().Exec()`, which is a different path (not exposed as a gRPC method).\n\nSee [pb/yao.proto](./pb/yao.proto) for full message definitions.\n\n## Process & Stream (gou foundation)\n\ngRPC `Run` and `Stream` map to two parallel systems in `gou`:\n\n```\ngou/process/   — execute once, return result     → gRPC Run\ngou/stream/    — execute once, push chunks        → gRPC Stream\n```\n\n### gou/process (existing, unchanged)\n\n```go\ntype Handler func(process *Process) interface{}\n\nprocess.Register(\"scripts\", handler)\np := process.New(\"scripts.foo.bar\", args...)\np.Execute()\nresult := p.Value()\n```\n\n### gou/stream (new package, parallel to process)\n\n```go\ntype Handler func(ctx context.Context, process *Process, send func([]byte) error) error\n\nstream.Register(\"scripts\", handler)\ns := stream.New(\"scripts.foo.bar\", args...)\ns.Execute(ctx, func(chunk []byte) error { ... })\n```\n\n`stream.Process` mirrors `process.Process` fields (Name, Group, Method, ID, Args, Global, Sid, Authorized) but `ctx` is a first-class parameter, not buried in a struct field.\n\n`send` returns error when the receiver disconnects — handler should stop.\n\n### Fallback\n\nIf a stream handler is not registered for a name but a process handler exists, `stream.Execute` falls back to: run the process handler once, JSON-marshal the result, call `send` once.\n\n### Registration\n\n```go\n// gou/process — existing\nprocess.Register(\"models\", modelsHandler)\nprocess.Register(\"scripts\", scriptsHandler)\n\n// gou/stream — new, same namespace\nstream.Register(\"scripts\", scriptsStreamHandler)\nstream.Register(\"llm\", llmStreamHandler)\n```\n\nSame naming convention. A process name can have both a process handler and a stream handler.\n\n### gRPC mapping\n\n```go\nfunc (s *yaoServer) Run(ctx context.Context, req *pb.RunRequest) (*pb.RunResponse, error) {\n    p := process.NewWithContext(ctx, req.Process, args...)\n    if err := p.Execute(); err != nil { return nil, err }\n    data, _ := json.Marshal(p.Value())\n    return &pb.RunResponse{Result: data}, nil\n}\n\nfunc (s *yaoServer) Stream(req *pb.RunRequest, grpcStream pb.Yao_StreamServer) error {\n    st := stream.New(req.Process, args...)\n    return st.Execute(grpcStream.Context(), func(chunk []byte) error {\n        return grpcStream.Send(&pb.Chunk{Data: chunk})\n    })\n}\n```\n\n### V8 integration\n\nBoth are exposed as top-level globals in JavaScript, parallel:\n\n```go\n// gou/runtime/v8/isolate.go MakeTemplate\ntemplate.Set(\"Process\", processModule.ExportFunction(iso))        // existing\ntemplate.Set(\"Stream\",  streamModule.ExportFunction(iso))         // new\n```\n\n**JS calling Go stream** (JS is consumer):\n\n```javascript\nStream(\"llm.chat.completions\", function(chunk) {\n    log.Info(chunk)\n    return 1  // 1=continue, 0=stop\n}, { model: \"gpt-4\", messages: [...] })\n```\n\n**JS script as stream handler** (JS is producer):\n\n```javascript\n// scripts/report.js — registered via stream.Register(\"scripts\", ...)\nfunction generate(args, send) {\n    send(\"part 1\")\n    send(\"part 2\")\n}\n```\n\nV8 runtime registers both:\n\n```go\nfunc init() {\n    process.Register(\"scripts\", processScripts)   // existing\n    stream.Register(\"scripts\", processScriptsStream) // new\n}\n```\n\n`processScriptsStream` calls `script.ExecStream(ctx, p, send)` which injects `send` into the V8 global before executing the script method.\n\n### Impact on existing code\n\n| Component | Changes |\n|-----------|---------|\n| `gou/process/` | None |\n| `gou/stream/` | New package (~150 lines) |\n| `gou/runtime/v8/process.go` | +1 line: `stream.Register(...)` |\n| `gou/runtime/v8/script.go` | +`ExecStream` method |\n| `gou/runtime/v8/isolate.go` | +1 line: `template.Set(\"Stream\", ...)` |\n| `gou/runtime/v8/functions/` | +`stream/` module for JS→Go stream consumption |\n","workspace/DESIGN.md":"# workspace - UI Design System & Style Tokens\n\nPath: `workspace/DESIGN.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/workspace/DESIGN.md)","tools/secret/connectors_schema.json":"# Secret Skill\n\nPath: `tools/secret/connectors_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/secret/connectors_schema.json)","tools/secret/list_schema.json":"# Secret Skill\n\nPath: `tools/secret/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/secret/list_schema.json)","tools/secret/read_schema.json":"# Secret Skill\n\nPath: `tools/secret/read_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/secret/read_schema.json)","tools/robot/create_schema.json":"# Robot Skill\n\nPath: `tools/robot/create_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/create_schema.json)","tools/robot/execution_cancel_schema.json":"# Robot Skill\n\nPath: `tools/robot/execution_cancel_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/execution_cancel_schema.json)","tools/robot/execution_create_schema.json":"# Robot Skill\n\nPath: `tools/robot/execution_create_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/execution_create_schema.json)","tools/robot/execution_get_schema.json":"# Robot Skill\n\nPath: `tools/robot/execution_get_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/execution_get_schema.json)","tools/robot/execution_list_schema.json":"# Robot Skill\n\nPath: `tools/robot/execution_list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/execution_list_schema.json)","tools/robot/get_schema.json":"# Robot Skill\n\nPath: `tools/robot/get_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/get_schema.json)","tools/robot/list_schema.json":"# Robot Skill\n\nPath: `tools/robot/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/list_schema.json)","tools/robot/result_list_schema.json":"# Robot Skill\n\nPath: `tools/robot/result_list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/result_list_schema.json)","tools/robot/status_schema.json":"# Robot Skill\n\nPath: `tools/robot/status_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/status_schema.json)","tools/robot/update_schema.json":"# Robot Skill\n\nPath: `tools/robot/update_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/update_schema.json)","tools/proc/allowed.json":"# Proc Skill\n\nPath: `tools/proc/allowed.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/proc/allowed.json)","tools/proc/schema.json":"# Proc Skill\n\nPath: `tools/proc/schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/proc/schema.json)","tools/mobile/exec_schema.json":"# Mobile Skill\n\nPath: `tools/mobile/exec_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/exec_schema.json)","tools/mobile/info_schema.json":"# Mobile Skill\n\nPath: `tools/mobile/info_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/info_schema.json)","tools/mobile/list_schema.json":"# Mobile Skill\n\nPath: `tools/mobile/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/list_schema.json)","tools/mobile/pull_schema.json":"# Mobile Skill\n\nPath: `tools/mobile/pull_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/pull_schema.json)","tools/mobile/push_schema.json":"# Mobile Skill\n\nPath: `tools/mobile/push_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/push_schema.json)","tools/mobile/screenshot_schema.json":"# Mobile Skill\n\nPath: `tools/mobile/screenshot_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/screenshot_schema.json)","tools/mcps/agent.json":"# Mcps Skill\n\nPath: `tools/mcps/agent.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/agent.json)","tools/mcps/audio.json":"# Mcps Skill\n\nPath: `tools/mcps/audio.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/audio.json)","tools/mcps/clip.json":"# Mcps Skill\n\nPath: `tools/mcps/clip.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/clip.json)","tools/mcps/doc.json":"# Mcps Skill\n\nPath: `tools/mcps/doc.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/doc.json)","tools/mcps/image.json":"# Mcps Skill\n\nPath: `tools/mcps/image.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/image.json)","tools/mcps/kanban.json":"# Mcps Skill\n\nPath: `tools/mcps/kanban.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/kanban.json)","tools/mcps/mobile.json":"# Mcps Skill\n\nPath: `tools/mcps/mobile.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/mobile.json)","tools/mcps/process.json":"# Mcps Skill\n\nPath: `tools/mcps/process.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/process.json)","tools/mcps/robot.json":"# Mcps Skill\n\nPath: `tools/mcps/robot.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/robot.json)","tools/mcps/secret.json":"# Mcps Skill\n\nPath: `tools/mcps/secret.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/secret.json)","tools/mcps/web.json":"# Mcps Skill\n\nPath: `tools/mcps/web.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/web.json)","tools/mcps/workspace.json":"# Mcps Skill\n\nPath: `tools/mcps/workspace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/workspace.json)","tools/mcps/workspace-config.json":"# Mcps Skill\n\nPath: `tools/mcps/workspace-config.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/workspace-config.json)","tools/inbox/list_schema.json":"# Inbox Skill\n\nPath: `tools/inbox/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/inbox/list_schema.json)","tools/inbox/view_schema.json":"# Inbox Skill\n\nPath: `tools/inbox/view_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/inbox/view_schema.json)","tools/image/edit_schema.json":"# Image Skill\n\nPath: `tools/image/edit_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/edit_schema.json)","tools/image/generate_schema.json":"# Image Skill\n\nPath: `tools/image/generate_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/generate_schema.json)","tools/image/providers_schema.json":"# Image Skill\n\nPath: `tools/image/providers_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/providers_schema.json)","tools/image/read_schema.json":"# Image Skill\n\nPath: `tools/image/read_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/read_schema.json)","tools/docs/inspect.json":"# Docs Skill\n\nPath: `tools/docs/inspect.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/docs/inspect.json)","tools/docs/list.json":"# Docs Skill\n\nPath: `tools/docs/list.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/docs/list.json)","tools/docs/validate.json":"# Docs Skill\n\nPath: `tools/docs/validate.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/docs/validate.json)","tools/clip/list_schema.json":"# Clip Skill\n\nPath: `tools/clip/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/clip/list_schema.json)","tools/clip/read_schema.json":"# Clip Skill\n\nPath: `tools/clip/read_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/clip/read_schema.json)","tools/clip/write_schema.json":"# Clip Skill\n\nPath: `tools/clip/write_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/clip/write_schema.json)","tools/board/create_schema.json":"# Board Skill\n\nPath: `tools/board/create_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/board/create_schema.json)","tools/board/list_schema.json":"# Board Skill\n\nPath: `tools/board/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/board/list_schema.json)","tools/audio/providers_schema.json":"# Audio Skill\n\nPath: `tools/audio/providers_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/audio/providers_schema.json)","tools/audio/transcribe_schema.json":"# Audio Skill\n\nPath: `tools/audio/transcribe_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/audio/transcribe_schema.json)","tools/agents/a2a.md":"# Subagent: a2a\n\nPath: `tools/agents/a2a.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agents/a2a.md)","tools/agent/call_schema.json":"# Agent Skill\n\nPath: `tools/agent/call_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/call_schema.json)","tools/agent/connectors_schema.json":"# Agent Skill\n\nPath: `tools/agent/connectors_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/connectors_schema.json)","tools/agent/deploy_schema.json":"# Agent Skill\n\nPath: `tools/agent/deploy_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/deploy_schema.json)","tools/agent/download_schema.json":"# Agent Skill\n\nPath: `tools/agent/download_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/download_schema.json)","tools/agent/list_schema.json":"# Agent Skill\n\nPath: `tools/agent/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/list_schema.json)","tools/agent/reference_schema.json":"# Agent Skill\n\nPath: `tools/agent/reference_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/reference_schema.json)","tools/task/create_schema.json":"# Task Skill\n\nPath: `tools/task/create_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/create_schema.json)","tools/task/list_schema.json":"# Task Skill\n\nPath: `tools/task/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/list_schema.json)","tools/task/move_schema.json":"# Task Skill\n\nPath: `tools/task/move_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/move_schema.json)","tools/task/run_schema.json":"# Task Skill\n\nPath: `tools/task/run_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/run_schema.json)","tools/task/stop_schema.json":"# Task Skill\n\nPath: `tools/task/stop_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/stop_schema.json)","tools/webfetch/schema.json":"# Webfetch Skill\n\nPath: `tools/webfetch/schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/webfetch/schema.json)","tools/websearch/schema.json":"# Websearch Skill\n\nPath: `tools/websearch/schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/websearch/schema.json)","tools/workspace/file_list_schema.json":"# Workspace Skill\n\nPath: `tools/workspace/file_list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/file_list_schema.json)","tools/workspace/file_read_schema.json":"# Workspace Skill\n\nPath: `tools/workspace/file_read_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/file_read_schema.json)","tools/workspace/file_write_schema.json":"# Workspace Skill\n\nPath: `tools/workspace/file_write_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/file_write_schema.json)","tools/workspace/get_schema.json":"# Workspace Skill\n\nPath: `tools/workspace/get_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/get_schema.json)","tools/workspace/list_schema.json":"# Workspace Skill\n\nPath: `tools/workspace/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/list_schema.json)","tools/workspace_config/git_config_schema.json":"# Workspace_config Skill\n\nPath: `tools/workspace_config/git_config_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace_config/git_config_schema.json)","tools/workspace_config/git_credential_schema.json":"# Workspace_config Skill\n\nPath: `tools/workspace_config/git_credential_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace_config/git_credential_schema.json)","tools/workspace_config/ssh_key_schema.json":"# Workspace_config Skill\n\nPath: `tools/workspace_config/ssh_key_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace_config/ssh_key_schema.json)"},"files":{"tools/skills/yao-process/SKILL.md":"---\nname: yao-process\ndescription: Yao process execution expert. ALWAYS invoke this skill when the user needs to call a Yao process, query data models, run scripts, or check process permissions. Do not call processes without checking this skill first.\n---\n\n# Process Tools\n\nTwo tools for Yao process execution, called via bash.\n\n## process_call\n\nExecute a Yao Process by its fully qualified name.\n\n```bash\ntai tool process_call '{\"name\": \"models.user.Find\", \"args\": [1, {}]}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `name` | string | yes | Process name (e.g. `models.user.Find`) |\n| `args` | array | no | Positional arguments |\n\n## process_allowed\n\nCheck which processes are permitted, or verify a specific process.\n\n```bash\n# List all allowed rules\ntai tool process_allowed '{}'\n\n# Check a specific process\ntai tool process_allowed '{\"name\": \"models.user.Find\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `name` | string | no | Process name to check. Omit to list all rules. |\n\n## Workflow\n\n1. Use **process_allowed** to check what is permitted\n2. Use **doc_inspect** (from yao-doc skill) to understand the process signature\n3. Use **process_call** to execute\n\n## Guidelines\n\n- Always check documentation before calling an unfamiliar process\n- A 403 error means the process is not in the allowed list\n- Process names follow the pattern `group.id.Method` (e.g. `models.user.Find`, `scripts.auth.Check`)\n- Rules use prefix matching: `models.*` matches all model processes\n","tools/skills/yao-secret/SKILL.md":"---\nname: yao-secret\ndescription: Secret management expert. ALWAYS invoke this skill when you need to read API keys, tokens, or other secrets configured by the user. Never hardcode credentials — use this skill to retrieve them securely.\n---\n\n# Secret Tools\n\nTwo tools for accessing user-configured secrets, called via bash.\n\n## secret_list\n\nList available secret names and descriptions. **Does not return secret values** — use `secret_read` for that.\n\n```bash\ntai tool secret_list '{}'\n```\n\nNo parameters required. Returns secrets configured for the current assistant.\n\n## secret_read\n\nRead a secret value by name. Returns the decrypted value for use in scripts.\n\n```bash\ntai tool secret_read '{\"name\": \"GITHUB_TOKEN\"}'\ntai tool secret_read '{\"name\": \"AWS_SECRET_KEY\"}'\n```\n\n| Parameter | Type   | Required | Description                                              |\n|-----------|--------|----------|----------------------------------------------------------|\n| `name`    | string | yes      | Secret key name (e.g. `GITHUB_TOKEN`, `AWS_SECRET_KEY`)  |\n\n**Security**: Never log, print, or expose the returned secret value in output visible to users.\n\n## Typical Workflow\n\n1. `secret_list` — discover what secrets are available\n2. `secret_read` — retrieve a specific secret by name\n3. Use the value in API calls, git auth, etc.\n\n## Guidelines\n\n- Always call `secret_list` first to check if a required secret exists before reading\n- Never hardcode API keys or tokens — always use `secret_read`\n- Secret values are decrypted at read time; treat them as sensitive\n- If a secret is not found, prompt the user to configure it in their settings\n- All output is JSON\n","tools/skills/yao-web/SKILL.md":"---\nname: yao-web\ndescription: Web information retrieval expert. ALWAYS invoke this skill when the user needs to search the web, fetch a URL, or access real-time information beyond training data. Do not guess or use stale knowledge — use this skill first.\n---\n\n# Web Tools\n\nTwo tools for web information retrieval, called via bash.\n\n## web_search\n\nSearch the web for real-time information. Returns structured results with title, URL, and content snippet.\n\n```bash\ntai tool web_search '{\"query\": \"search terms\", \"limit\": 5}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `query` | string | yes | Search query |\n| `limit` | integer | no | Max results (default 10) |\n\n## web_fetch\n\nFetch a web page and return its content in readable format.\n\n```bash\ntai tool web_fetch '{\"url\": \"https://example.com\", \"format\": \"markdown\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `url` | string | yes | Fully-formed URL to fetch |\n| `format` | string | no | `markdown` (default) or `html` |\n\n## Guidelines\n\n- Use web_search to discover URLs, then web_fetch to read the content\n- Include the current year when searching for recent information\n- All output is JSON\n","tools/secret/SKILL.md":"# Secret Management\n\nAgents can read user-configured secrets at runtime using the `tai tool` CLI.\nSecrets are encrypted at rest (AES-256-GCM) and decrypted only when read.\n\n## Available Tools\n\n| Tool | Description |\n|------|-------------|\n| `secret_list` | List secret names and descriptions (no values) |\n| `secret_read` | Read a single secret value by name |\n\n## Usage\n\n### Bash\n\n```bash\n# List available secrets\ntai tool secret_list\n\n# Read a secret\nTOKEN=$(tai tool secret_read '{\"name\": \"GITHUB_TOKEN\"}' | jq -r '.value')\ngit clone \"https://${TOKEN}@github.com/org/repo.git\"\n```\n\n### Node.js\n\n```javascript\nconst { execSync } = require(\"child_process\");\n\nfunction readSecret(name) {\n  const raw = execSync(\n    `tai tool secret_read '${JSON.stringify({ name })}'`,\n    { encoding: \"utf-8\" }\n  );\n  return JSON.parse(raw).value;\n}\n\nconst token = readSecret(\"GITHUB_TOKEN\");\n```\n\n### Python\n\n```python\nimport json\nimport subprocess\n\ndef read_secret(name: str) -> str:\n    result = subprocess.run(\n        [\"tai\", \"tool\", \"secret_read\", json.dumps({\"name\": name})],\n        capture_output=True, text=True, check=True,\n    )\n    return json.loads(result.stdout)[\"value\"]\n\ntoken = read_secret(\"GITHUB_TOKEN\")\n```\n\n### PowerShell\n\n```powershell\nfunction Read-Secret {\n    param([string]$Name)\n    $json = @{ name = $Name } | ConvertTo-Json -Compress\n    $result = tai tool secret_read $json | ConvertFrom-Json\n    return $result.value\n}\n\n$token = Read-Secret -Name \"GITHUB_TOKEN\"\n```\n\n## Security Rules\n\n1. **Never print or log secret values** — Do not write secrets to stdout, stderr, or any log file.\n2. **Never write secrets to files** — Exception: SSH keys may be written to `~/.ssh/` with `chmod 600` permissions.\n3. **Never send secrets to the LLM** — Secret values must not appear in prompt content, system messages, or tool call results that are forwarded to the model.\n4. **Scope isolation** — Secrets are scoped per user per agent. An agent can only access secrets configured for it.\n5. **Audit trail** — Every `secret_read` call is logged in the audit trail with the caller's identity.\n","tools/skills/yao-workspace-config/SKILL.md":"---\nname: yao-workspace-config\ndescription: \"Workspace Git identity and credential management. ALWAYS invoke this skill when the user asks about configuring Git user info, adding HTTPS tokens, importing SSH keys, or managing workspace-level Git authentication.\"\n---\n\n# Workspace Git Config Tools\n\nManage workspace-level Git configuration, HTTPS credentials, and SSH keys.\n\n## workspace_git_config\n\nGet or set workspace-level Git configuration values.\n\n```bash\n# Get all config\ntai tool workspace_git_config '{\"workspace_id\":\"ws-xxx\",\"action\":\"get\"}'\n\n# Get specific key\ntai tool workspace_git_config '{\"workspace_id\":\"ws-xxx\",\"action\":\"get\",\"key\":\"user.name\"}'\n\n# Set config\ntai tool workspace_git_config '{\"workspace_id\":\"ws-xxx\",\"action\":\"set\",\"key\":\"user.name\",\"value\":\"John\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| workspace_id | string | yes | Workspace ID |\n| action | string | yes | `get` or `set` |\n| key | string | no | Config key (e.g. `user.name`). Empty returns all. |\n| value | string | for set | Config value |\n\n## workspace_git_credential\n\nManage HTTPS Git credentials (personal access tokens).\n\n```bash\n# Set credential\ntai tool workspace_git_credential '{\"workspace_id\":\"ws-xxx\",\"action\":\"set\",\"host\":\"github.com\",\"token\":\"ghp_xxx\"}'\n\n# List credentials (tokens are not exposed)\ntai tool workspace_git_credential '{\"workspace_id\":\"ws-xxx\",\"action\":\"list\"}'\n\n# Delete credential\ntai tool workspace_git_credential '{\"workspace_id\":\"ws-xxx\",\"action\":\"delete\",\"host\":\"github.com\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| workspace_id | string | yes | Workspace ID |\n| action | string | yes | `set`, `list`, or `delete` |\n| host | string | for set/delete | Git host (e.g. `github.com`) |\n| username | string | no | Username (defaults to `x-access-token`) |\n| token | string | for set | Access token |\n\n## workspace_ssh_key\n\nManage SSH keys for Git authentication.\n\n```bash\n# Import SSH key\ntai tool workspace_ssh_key '{\"workspace_id\":\"ws-xxx\",\"action\":\"import\",\"name\":\"github\",\"private_key\":\"-----BEGIN...\",\"host\":\"github.com\"}'\n\n# List SSH keys (private keys not exposed)\ntai tool workspace_ssh_key '{\"workspace_id\":\"ws-xxx\",\"action\":\"list\"}'\n\n# Delete SSH key\ntai tool workspace_ssh_key '{\"workspace_id\":\"ws-xxx\",\"action\":\"delete\",\"name\":\"github\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| workspace_id | string | yes | Workspace ID |\n| action | string | yes | `import`, `list`, or `delete` |\n| name | string | for import/delete | Key name |\n| private_key | string | for import | PEM-encoded private key |\n| public_key | string | no | Public key (derived from private_key if omitted) |\n| host | string | no | Git host to associate (e.g. `github.com`) |\n","tools/skills/yao-workspace/SKILL.md":"---\nname: yao-workspace\ndescription: Workspace file I/O expert. ALWAYS invoke this skill when you need to list workspaces, read or write files in a workspace on a remote node, or browse workspace directories. Use this for cross-node file operations — for local sandbox files, use standard filesystem tools instead.\n---\n\n# Workspace Tools\n\nFive tools for managing workspaces and file I/O, called via bash.\n\n## workspace_list\n\nList workspaces with summary info. Use `workspace_get` for full details.\n\n```bash\ntai tool workspace_list '{}'\ntai tool workspace_list '{\"node\": \"tai-node-1\"}'\n```\n\n| Parameter | Type   | Required | Description              |\n|-----------|--------|----------|--------------------------|\n| `node`    | string | no       | Filter by Tai node ID    |\n\n## workspace_get\n\nGet full workspace details: name, owner, node, labels, timestamps.\n\n```bash\ntai tool workspace_get '{\"id\": \"ws-abc123\"}'\n```\n\n| Parameter | Type   | Required | Description   |\n|-----------|--------|----------|---------------|\n| `id`      | string | yes      | Workspace ID  |\n\n## workspace_file_list\n\nList files and directories in a workspace path.\n\n```bash\ntai tool workspace_file_list '{\"id\": \"ws-abc123\"}'\ntai tool workspace_file_list '{\"id\": \"ws-abc123\", \"path\": \"src/components\"}'\n```\n\n| Parameter | Type   | Required | Description                    |\n|-----------|--------|----------|--------------------------------|\n| `id`      | string | yes      | Workspace ID                   |\n| `path`    | string | no       | Directory path (default \".\")   |\n\n## workspace_file_read\n\nRead file content from workspace. Returns text (UTF-8) or base64 encoded.\n\n```bash\ntai tool workspace_file_read '{\"id\": \"ws-abc123\", \"path\": \"README.md\"}'\ntai tool workspace_file_read '{\"id\": \"ws-abc123\", \"path\": \"image.png\", \"encoding\": \"base64\"}'\n```\n\n| Parameter  | Type   | Required | Description                              |\n|------------|--------|----------|------------------------------------------|\n| `id`       | string | yes      | Workspace ID                             |\n| `path`     | string | yes      | File path relative to workspace root     |\n| `encoding` | string | no       | \"text\" (default) or \"base64\"             |\n\n## workspace_file_write\n\nWrite content to a file in workspace. For binary files, use base64 encoding.\n\n```bash\ntai tool workspace_file_write '{\"id\": \"ws-abc123\", \"path\": \"output.txt\", \"content\": \"Hello World\"}'\ntai tool workspace_file_write '{\"id\": \"ws-abc123\", \"path\": \"data.bin\", \"content\": \"base64data...\", \"encoding\": \"base64\"}'\n```\n\n| Parameter  | Type   | Required | Description                              |\n|------------|--------|----------|------------------------------------------|\n| `id`       | string | yes      | Workspace ID                             |\n| `path`     | string | yes      | File path relative to workspace root     |\n| `content`  | string | yes      | File content (text or base64 encoded)    |\n| `encoding` | string | no       | \"text\" (default) or \"base64\"             |\n\n## Typical Workflow\n\n1. `workspace_list` — discover available workspaces\n2. `workspace_get` — get full details (node, labels)\n3. `workspace_file_list` — browse directory structure\n4. `workspace_file_read` — read specific files\n5. `workspace_file_write` — write output files\n\n## Guidelines\n\n- Use `workspace_list` for discovery, `workspace_get` for details (list returns summaries only)\n- For large files (>1MB), prefer local filesystem operations if the workspace is mounted in the sandbox\n- Use `encoding: \"base64\"` for binary files (images, archives, etc.)\n- File paths are relative to the workspace root — do not use absolute paths\n- The current sandbox workspace ID is available via `$CTX_WORKSPACE_ID`\n- All output is JSON\n","tools/skills/yao-image/SKILL.md":"---\nname: yao-image\ndescription: Image expert. ALWAYS invoke this skill when you need to read, analyze, describe, or generate images. Use for screenshots, photos, charts, diagrams, AI-generated images, or any visual content.\n---\n\n# Image Tools\n\nUse these tools when you encounter images you cannot read natively, or when you need to generate new images.\n\n## image_read\n\nSend an image to a vision-capable model and get a text description.\n\n### Local file (most common):\n```bash\ntai tool image_read --image_path /path/to/image.png --prompt \"Describe this image\"\n```\n\n### URL:\n```bash\ntai tool image_read --image_path https://example.com/photo.jpg --prompt \"What is shown?\"\n```\n\n### With a specific vision provider:\n```bash\ntai tool image_read --image_path /path/to/image.png --prompt \"Describe\" --provider llm.my-openai:gpt-4o\n```\n\n| Parameter  | Type    | Required | Description                                                     |\n| ---------- | ------- | -------- | --------------------------------------------------------------- |\n| image_path | string  | yes      | Image file path or URL                                          |\n| prompt     | string  | no       | Analysis instruction (default: describe in detail)              |\n| max_size   | integer | no       | Max dimension in pixels for longest edge (default: 1080)        |\n| provider   | string  | no       | Vision provider connector ID. If omitted, uses default vision model |\n\nImages are automatically resized (preserving aspect ratio) before sending to the vision model.\nSupported formats: PNG, JPEG, GIF, WebP.\n\n## image_generate\n\nGenerate a new image from a text prompt (text-to-image). For editing an existing image, use `image_edit` instead.\n\n### Basic usage (always specify output):\n```bash\ntai tool image_generate --prompt \"A serene mountain landscape at sunset\" --output landscape.png\n```\n\n### With specific provider, model and size:\n```bash\ntai tool image_generate --prompt \"A futuristic city skyline\" --provider llm.my-openai --model gpt-image-1 --dimensions 1792x1024 --output output/city.png\n```\n\n### Transparent background (for icons, stickers, product shots):\n```bash\ntai tool image_generate --prompt \"A cute fox mascot\" --background transparent --output_format png --output fox.png\n```\n\n### WebP output with quality control:\n```bash\ntai tool image_generate --prompt \"A landscape painting\" --output_format webp --quality high --output painting.webp\n```\n\n| Parameter | Type   | Required | Description                                                       |\n| --------- | ------ | -------- | ----------------------------------------------------------------- |\n| prompt     | string | yes      | Text description of the image to generate                         |\n| output     | string | yes      | Output file path for the generated image                          |\n| provider   | string | no       | Provider connector ID (use `image_providers` to list). Auto-selects if omitted |\n| dimensions | string | no       | Image dimensions (default: 1024x1024). Common: 1024x1024, 1024x1792, 1792x1024 |\n| model      | string | no       | Model name to use. Overrides the provider's default model         |\n| background | string | no       | `transparent`, `opaque`, or `auto`. Use `transparent` for PNG/WebP with no background |\n| output_format | string | no    | `png`, `jpeg`, or `webp`. Default: png                            |\n| output_compression | integer | no | Compression level 0-100 for jpeg/webp. Higher = better quality. Default: 100 |\n| quality    | string | no       | `low`, `medium`, `high`, or `auto`. Higher takes longer. Default: auto |\n| extra      | JSON   | no       | Provider-specific parameters as a JSON object (e.g. `--extra '{\"moderation\":\"low\"}'`) |\n\nIf `output` is omitted, the image is saved to a default path in the working directory.\n\n## image_edit\n\nEdit or transform an existing image based on a text prompt (image-to-image). Use for style transfer, background replacement, adding/removing elements, or any modification that requires a reference image.\n\n### Basic usage:\n```bash\ntai tool image_edit --image_path /path/to/photo.png --prompt \"Change the background to a beach scene\" --output edited.png\n```\n\n### With URL image:\n```bash\ntai tool image_edit --image_path https://example.com/photo.jpg --prompt \"Make it look like a watercolor painting\" --output watercolor.png\n```\n\n### With specific provider and model:\n```bash\ntai tool image_edit --image_path /path/to/original.png --prompt \"Remove the person in the foreground\" --provider llm.my-openai --model gpt-image-1 --dimensions 1024x1024 --output result.png\n```\n\n### With mask (edit only the masked region):\n```bash\ntai tool image_edit --image_path /path/to/photo.png --mask /path/to/mask.png --prompt \"Replace with a garden\" --output edited.png\n```\n\n### Transparent background edit:\n```bash\ntai tool image_edit --image_path /path/to/product.png --prompt \"Remove background\" --background transparent --output_format png --output cutout.png\n```\n\n| Parameter  | Type   | Required | Description                                                       |\n| ---------- | ------ | -------- | ----------------------------------------------------------------- |\n| image_path | string | yes      | Reference image file path or URL                                  |\n| prompt     | string | yes      | Text description of the desired edit or transformation            |\n| output     | string | yes      | Output file path for the edited image                             |\n| provider   | string | no       | Provider connector ID (use `image_providers` with `capability=image_editing`). Auto-selects if omitted |\n| dimensions | string | no       | Output dimensions (default: 1024x1024). Common: 1024x1024, 1024x1792, 1792x1024 |\n| model      | string | no       | Model name to use. Overrides the provider's default model         |\n| mask       | string | no       | Mask image path/URL. Transparent areas in the mask define the editable region |\n| background | string | no       | `transparent`, `opaque`, or `auto`. Use `transparent` for PNG/WebP with no background |\n| output_format | string | no    | `png`, `jpeg`, or `webp`. Default: png                            |\n| output_compression | integer | no | Compression level 0-100 for jpeg/webp. Higher = better quality. Default: 100 |\n| quality    | string | no       | `low`, `medium`, `high`, or `auto`. Higher takes longer. Default: auto |\n| extra      | JSON   | no       | Provider-specific parameters as a JSON object (e.g. `--extra '{\"input_fidelity\":\"high\"}'`) |\n\nIf `output` is omitted, the image is saved to a default path in the working directory.\n\n## image_providers\n\nList available image providers filtered by capability.\n\n### List image generation providers (default):\n```bash\ntai tool image_providers\n```\n\n### List image editing providers:\n```bash\ntai tool image_providers --capability image_editing\n```\n\n### List vision (image reading) providers:\n```bash\ntai tool image_providers --capability vision\n```\n\n| Parameter  | Type   | Required | Description                                                 |\n| ---------- | ------ | -------- | ----------------------------------------------------------- |\n| capability | string | no       | `image_generation` (default), `image_editing`, or `vision`  |\n\nReturns a list of providers with their available models and connector IDs that can be passed to `image_generate`, `image_edit`, or `image_read`.\n\n## Constraints\n\nUse only the parameters listed above for each tool. The supported first-class parameters are: `prompt`, `output`, `provider`, `dimensions`, `model`, `background`, `output_format`, `output_compression`, `quality`, `mask` (edit only), and `extra`.\n\nDo **not** pass `n`, `style`, or `response_format` — they are unsupported and will be ignored or cause errors.\n\nFor provider-specific parameters not covered above (e.g. `moderation`, `input_fidelity`), pass them through the `extra` JSON object.\n","tools/skills/yao-audio/SKILL.md":"---\nname: yao-audio\ndescription: Audio expert. ALWAYS invoke this skill when the user asks to transcribe, recognize, or convert speech/audio to text.\n---\n\n# Audio Tools\n\nUse these tools to transcribe audio files to text using speech-to-text models.\n\n## audio_transcribe\n\nTranscribe an audio file to text.\n\n```bash\ntai tool audio_transcribe --audio_path /path/to/meeting.m4a\n```\n\n```bash\ntai tool audio_transcribe --audio_path /path/to/recording.wav --language en --provider llm.my-openai:whisper-1\n```\n\n| Parameter  | Type   | Required | Description                                                       |\n| ---------- | ------ | -------- | ----------------------------------------------------------------- |\n| audio_path | string | yes      | Audio file path. Supported: mp3, m4a, wav, webm, mp4, mpeg, mpga  |\n| language   | string | no       | ISO 639-1 language code (e.g. `en`, `zh`, `ja`). Auto-detected if omitted |\n| provider   | string | no       | STT provider connector ID. If omitted, uses the default STT provider |\n\n## audio_providers\n\nList available speech-to-text providers and models.\n\n### List STT providers (default):\n```bash\ntai tool audio_providers\n```\n\n| Parameter  | Type   | Required | Description                          |\n| ---------- | ------ | -------- | ------------------------------------ |\n| capability | string | no       | Filter by capability (default: `audio`) |\n\nReturns a list of providers with their available models and connector IDs that can be passed to `audio_transcribe`.\n\n## Constraints\n\nOnly use the parameters listed above for each tool. Do not pass unsupported parameters — they will be ignored or cause errors.\n","tools/skills/yao-doc/SKILL.md":"---\nname: yao-doc\ndescription: Yao process documentation expert. ALWAYS invoke this skill when the user needs to discover available processes, read process signatures, or validate process names. Do not guess process APIs — use this skill first.\n---\n\n# Documentation Tools\n\nThree tools for browsing Yao process documentation, called via bash.\n\n## doc_list\n\nSearch and list available process documentation entries.\n\n```bash\ntai tool doc_list '{\"keyword\": \"user\", \"limit\": 10}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `keyword` | string | no | Search keyword (empty to list all) |\n| `limit` | integer | no | Max results (default 20) |\n\n## doc_inspect\n\nGet detailed documentation for a specific process: arguments, return type, methods.\n\n```bash\ntai tool doc_inspect '{\"name\": \"models.user.Find\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `name` | string | yes | Process name (e.g. `models.user.Find`) |\n\n## doc_validate\n\nCheck if a process name is valid. Returns suggestions for similar processes if not found.\n\n```bash\ntai tool doc_validate '{\"name\": \"models.user.Findd\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `name` | string | yes | Process name to validate |\n\n## Workflow\n\n1. **doc_list** — discover available processes by keyword\n2. **doc_inspect** — read the full signature before calling\n3. **doc_validate** — fix typos when a process name doesn't work\n\n## Guidelines\n\n- Always use doc_inspect before calling an unfamiliar process via process_call\n- Use doc_validate when you get unexpected errors — the name might be misspelled\n","tools/skills/yao-board/SKILL.md":"---\nname: yao-board\ndescription: Kanban board and task query expert. ALWAYS invoke this skill when the user asks about boards, tasks, task status, or project progress. Do not guess task state — use this skill first.\n---\n\n# Board & Task Tools\n\nTwo tools for querying kanban boards and tasks, called via bash.\n\n## board_list\n\nList all kanban boards with their columns and task counts.\n\n```bash\ntai tool board_list '{}'\n```\n\n## task_list\n\nList tasks with optional filtering.\n\n```bash\ntai tool task_list '{\"board_id\": \"xxx\", \"run_status\": \"running\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `run_status` | string | no | Filter: pending/running/waiting/completed/failed |\n| `assistant_id` | string | no | Filter by assistant ID |\n| `board_id` | string | no | Filter by board ID |\n| `page` | number | no | Page number (default 1) |\n| `page_size` | number | no | Items per page (default 50) |\n\n## Guidelines\n\n- Use board_list first to discover available boards and their IDs\n- Use task_list with filters to find specific tasks\n- All output is JSON\n","tools/skills/yao-agent/SKILL.md":"---\nname: yao-agent\ndescription: Agent management expert. ALWAYS invoke this skill when you need to list available agents, download or reference agent source code, deploy agent code to the host, or query the LLM connector matrix. Do not guess agent structures — use this skill first.\n---\n\n# Agent Tools\n\nFive tools for managing agents on the host, called via bash.\n\n## agent_list\n\nList available agents. Returns ID, name, description, and capabilities for each agent.\n\n```bash\ntai tool agent_list '{}'\ntai tool agent_list '{\"namespace\": \"smith\"}'\n```\n\n| Parameter   | Type   | Required | Description                                              |\n|-------------|--------|----------|----------------------------------------------------------|\n| `namespace` | string | no       | Filter by namespace (e.g. `yao`, `smith`). Omit for all. |\n\n## agent_download\n\nDownload a **smith-namespace** agent into the development directory for editing. **Restricted to `smith` namespace only** — for other agents, use `agent_reference`.\n\n```bash\ntai tool agent_download '{\"id\": \"smith.weather\"}'\n```\n\n| Parameter | Type   | Required | Description                                              |\n|-----------|--------|----------|----------------------------------------------------------|\n| `id`      | string | yes      | Agent ID in dot notation. Must be `smith.*`.             |\n\nDownloaded code lands in `agent-smith-dev/assistants/smith/<name>/`.\n\n## agent_reference\n\nDownload agent source code from the host into `.references/` for **read-only study**. Any agent across all namespaces can be referenced.\n\n```bash\ntai tool agent_reference '{\"id\": \"yao.slides\"}'\ntai tool agent_reference '{\"id\": \"yao.keeper\"}'\n```\n\n| Parameter | Type   | Required | Description                                        |\n|-----------|--------|----------|----------------------------------------------------|\n| `id`      | string | yes      | Agent ID in dot notation (e.g. `yao.slides`)       |\n\nReferenced code lands in `agent-smith-dev/.references/<namespace>/<name>/`.\n\n## agent_deploy\n\nDeploy agent source code from the sandbox development directory to the host. **Restricted to the `smith` namespace only** — attempts to deploy to other namespaces will be rejected.\n\n```bash\ntai tool agent_deploy '{\"id\": \"smith.weather\"}'\ntai tool agent_deploy '{\"id\": \"smith.weather\", \"message\": \"add SUI page\"}'\n```\n\n| Parameter | Type   | Required | Description                                            |\n|-----------|--------|----------|--------------------------------------------------------|\n| `id`      | string | yes      | Agent ID in dot notation. Must use `smith` namespace.  |\n| `message` | string | no       | Optional deploy message for logging.                   |\n\n## agent_connectors\n\nGet the current user's LLM connector matrix. Returns metadata for each role (default, heavy, light, vision, etc.) **without API keys**. Use this to understand which models are available and their capabilities.\n\n```bash\ntai tool agent_connectors '{}'\n```\n\nNo parameters required.\n\n## Guidelines\n\n- Use `agent_list` to discover agents before downloading or referencing\n- `agent_download` is for editing smith agents — code lands in `agent-smith-dev/assistants/smith/<name>/`\n- `agent_reference` is for studying any agent — code lands in `agent-smith-dev/.references/<namespace>/<name>/`\n- Deploy is restricted to the `smith` namespace for safety\n- Connector data never includes API keys, secrets, or tokens\n- All output is JSON\n","grpc/DESIGN.md":"# Yao gRPC Server\n\nGeneral-purpose gRPC gateway for the Yao process. Shares OAuth + ACL scope system with openapi — one token, two protocols.\n\n## Services\n\n| Layer | Method | Purpose | Scope |\n|-------|--------|---------|-------|\n| **Base** | `Run` | Execute Yao process, return result | `grpc:run` |\n| | `Stream` | Execute Yao process, stream output | `grpc:stream` |\n| | `Shell` | Execute system command, wait for result | `grpc:shell` |\n| | `ShellStream` | Execute system command, stream stdout/stderr | `grpc:shell` |\n| **API** | `API` | Proxy to openapi, any endpoint | openapi's own scopes |\n| **MCP** | `MCPListTools` | List MCP tools for a session | `grpc:mcp` |\n| | `MCPCallTool` | Call MCP tool → process.Exec() | `grpc:mcp` |\n| | `MCPListResources` | List MCP resources | `grpc:mcp` |\n| | `MCPReadResource` | Read MCP resource | `grpc:mcp` |\n| **LLM** | `ChatCompletions` | Send messages to LLM, get response | `grpc:llm` |\n| | `ChatCompletionsStream` | Stream LLM response (SSE → gRPC stream) | `grpc:llm` |\n| **Agent** | `AgentStream` | Call agent, stream response | `grpc:agent` |\n\n## Clients\n\n- Container MCP tools (via Tai gRPC relay)\n- `yao run` CLI (after `yao login`)\n- Yao-to-Yao (cross-node process execution)\n\n## Auth\n\nSame as openapi. gRPC auth interceptor reuses the same `guard.Authenticate` logic — including automatic token refresh when access token is expired but refresh token is valid.\n\n```\nmetadata (Bearer + x-refresh-token)\n    → VerifyToken\n    → expired? → TryRefresh (same as guard.go) → new tokens in response metadata\n    → extract scopes → acl.Scope.Check(method, path, scopes)\n```\n\n### Infrastructure reuse assessment\n\nExisting openapi/oauth infrastructure can be reused for gRPC with **zero modifications**:\n\n| Component | Reusable as-is | Notes |\n|-----------|---------------|-------|\n| `VerifyToken(token string)` | Yes | Pure string input, no Gin dependency |\n| `MakeAccessToken(clientID, scope, subject, expiresIn, extraClaims...)` | Yes | Supports custom scope/subject for container tokens |\n| `MakeRefreshToken(...)` | Yes | Same as above |\n| `Revoke(ctx, token, tokenTypeHint)` | Yes | For container token cleanup on Remove |\n| `ScopeManager.Check(req *AccessRequest)` | Yes | Only needs `(Method, Path, Scopes)` — no Gin dependency |\n| `acl.Register(...)` | Yes | gRPC scopes registered via same pattern |\n\nThe `authorized.SetInfo` / `authorized.GetInfo` are Gin-bound but **not needed** — gRPC interceptor builds `AccessRequest` directly from JWT claims. Full `Enforce` chain (client/team/member) is HTTP multi-tenant only; gRPC uses `VerifyToken → ScopeManager.Check` which is sufficient.\n\nNew code required: ~80 lines (interceptor + scope registration). Existing code changes: **zero**.\n\n### CLI auth: `yao login` / `yao logout`\n\nOAuth 2.0 Device Authorization Grant. No `--remote` flag needed — logged in = gRPC, not logged in = local.\n\n```\n$ yao login --server https://yao.example.com\n请访问: https://yao.example.com/device\n输入代码: ABCD-1234\n等待授权... ✓ (token saved to ~/.yao/credentials)\n\n$ yao run models.user.Find '{\"id\":1}'    ← auto gRPC\n$ yao logout\n```\n\nRequires two new openapi endpoints:\n- `POST /oauth/device/authorize` — issue device_code + user_code\n- `POST /oauth/device/token` — poll for access_token\n\nToken scope: based on user's role, e.g. `grpc:run grpc:stream grpc:shell grpc:llm grpc:agent grpc:mcp`.\n\n**Implementation cost**: ~190 lines new code, ~10 lines changes to existing code.\nScaffolding already in place — `types.DeviceAuthorizationResponse`, `GrantTypeDeviceCode`, error codes (`ErrorAuthorizationPending`, `ErrorSlowDown`), `DeviceCodeLifetime` config, `DeviceAuthorization()` method signature, and HTTP route are all pre-defined. Core work:\n\n1. Implement `DeviceAuthorization()` in `device.go` (currently returns `nil, nil`)\n2. Add device_code store/get/consume helpers in `token.go`\n3. Add `GrantTypeDeviceCode` case to `Token()` switch in `core.go` (1 case branch)\n4. Implement `handleDeviceCodeGrant()` in `core.go`\n5. Add user authorization callback handler\n6. Fix discovery endpoint path inconsistency (`/oauth/device` vs `/oauth/device_authorization`)\n\nRisk: **very low** — all additions are in isolated code paths, no changes to existing `authorization_code` / `client_credentials` / `refresh_token` flows.\n\n### Container token\n\nContainer images and `yao-grpc` (`yao/tai/grpc/`) are ours — it handles token refresh automatically.\n\n```\nManager creates container\n    ├─ oauth.MakeAccessToken(subject=userID, scope=\"grpc:mcp grpc:run\")\n    ├─ oauth.MakeRefreshToken(...)\n    └─ tai.Client.Sandbox().Create(CreateRequest{\n           Env: {\n               YAO_TOKEN, YAO_REFRESH_TOKEN, YAO_SANDBOX_ID,\n               YAO_GRPC_ADDR,                 // where to connect\n               YAO_GRPC_UPSTREAM,             // remote only: where Tai should forward to\n           },\n       })\n\n       Local:  YAO_GRPC_ADDR=127.0.0.1:9099    (direct to Yao, no upstream needed)\n       Remote: YAO_GRPC_ADDR=tai-host:9100      YAO_GRPC_UPSTREAM=yao-host:9099\n\nyao-grpc (tai/grpc/, container-internal)\n    ├─ reads YAO_GRPC_ADDR + YAO_TOKEN + YAO_REFRESH_TOKEN + YAO_SANDBOX_ID from env\n    ├─ if YAO_GRPC_UPSTREAM set: attaches x-grpc-upstream metadata (tells Tai where to forward)\n    ├─ every call: Bearer token + x-refresh-token + x-sandbox-id in gRPC metadata\n    ├─ server auth interceptor reuses guard.Authenticate logic:\n    │   token valid → pass through\n    │   token expired + refresh token present → auto rotate (same as HTTP guard)\n    │   new tokens returned via response metadata (x-access-token, x-refresh-token)\n    ├─ yao-grpc reads response metadata, updates tokens in memory\n    └─ transparent to caller, no separate refresh RPC needed\n```\n\n- access_token: short TTL (15m)\n- refresh_token: no expiry (valid until container removed)\n- Manager revokes refresh_token on container Remove\n- Tai does NOT know Yao address at startup — yao-grpc carries target in request metadata\n\n### Virtual endpoint mapping\n\n| gRPC | Virtual endpoint |\n|------|-----------------|\n| Run(\"models.user.Find\") | `POST /grpc/run/models.user.Find` |\n| Stream(\"flows.report\") | `POST /grpc/stream/flows.report` |\n| Shell | `POST /grpc/shell` |\n| ShellStream | `POST /grpc/shell` (same) |\n| API(POST, /kb/collections) | `POST /kb/collections` (real openapi path) |\n| MCPListTools | `GET /grpc/mcp/tools` |\n| MCPCallTool(\"search\") | `POST /grpc/mcp/call/search` |\n| MCPListResources | `GET /grpc/mcp/resources` |\n| MCPReadResource(\"uri\") | `GET /grpc/mcp/resources/read` |\n| ChatCompletions | `POST /grpc/llm/completions` |\n| ChatCompletionsStream | `POST /grpc/llm/completions` (same) |\n| AgentStream(\"robot-id\") | `POST /grpc/agent/robot-id` |\n\nAPI method uses the **actual openapi path** — no virtual mapping needed, scope check is identical to HTTP.\n\n### Scope registration\n\n```go\nfunc init() {\n    acl.Register(\n        &acl.ScopeDefinition{Name: \"grpc:run\",    Endpoints: []string{\"POST /grpc/run/*\"}},\n        &acl.ScopeDefinition{Name: \"grpc:stream\", Endpoints: []string{\"POST /grpc/stream/*\"}},\n        &acl.ScopeDefinition{Name: \"grpc:shell\",  Endpoints: []string{\"POST /grpc/shell\"}},\n        &acl.ScopeDefinition{Name: \"grpc:mcp\",    Endpoints: []string{\"GET /grpc/mcp/tools\", \"POST /grpc/mcp/call/*\", \"GET /grpc/mcp/resources\", \"GET /grpc/mcp/resources/read\"}},\n        &acl.ScopeDefinition{Name: \"grpc:llm\",    Endpoints: []string{\"POST /grpc/llm/completions\"}},\n        &acl.ScopeDefinition{Name: \"grpc:agent\", Endpoints: []string{\"POST /grpc/agent/*\"}},\n    )\n}\n```\n\n## Network\n\n### Server listen config\n\n| Env | Default | Purpose |\n|-----|---------|---------|\n| `YAO_GRPC_HOST` | `127.0.0.1` | Comma-separated bind addresses. |\n| `YAO_GRPC_PORT` | `9099` | Listen port (shared by all addresses). |\n| `YAO_GRPC` | _(unset)_ | Set `off` to explicitly disable gRPC server. |\n\ngRPC server **defaults to enabled** (`127.0.0.1:9099`) — sandbox container callbacks depend on it.\n\n`YAO_GRPC_HOST` accepts one or more addresses separated by `,`. Each address gets its own `net.Listener`; all listeners feed into the same `grpc.Server` (gRPC supports multiple `Serve` calls on one server).\n\n| Scenario | Config | Effect |\n|----------|--------|--------|\n| Local dev / default | _(nothing to set)_ | `127.0.0.1:9099` — loopback, sandbox works out of box |\n| LAN multi-NIC | `YAO_GRPC_HOST=192.168.10.1,10.0.0.1` | Binds each internal IP |\n| Open | `YAO_GRPC_HOST=0.0.0.0` | All interfaces |\n| Disabled | `YAO_GRPC=off` | gRPC server not started (pure API gateway, no sandbox) |\n\nWhen multiple addresses are given, the server creates one goroutine per listener. Shutdown (`grpc.GracefulStop`) drains all listeners.\n\nConfig lives in `config.Config.GRPC` (type `GRPCConfig`), same pattern as `Host`/`Port` for HTTP.\n\n### Startup\n\ngRPC server starts **after** HTTP server in `cmd/start.go`, as a parallel goroutine:\n\n```\nengine.Load → itask.Start → ischedule.Start → service.Start (HTTP) → grpc.StartServer (gRPC)\n```\n\ngRPC server starts by default. Set `YAO_GRPC=off` to explicitly disable (no-op startup). Any other value or unset means enabled.\n\nShutdown: `defer grpc.Stop()` in `cmd/start.go`, called before HTTP stop for graceful drain.\n\n### Access control\n\nLocal: containers and CLI connect via loopback. Remote: only Tai relay connects (address known from `YAO_TAI_ADDR`). All callers carry OAuth tokens — no IP allowlist needed.\n\nInterceptor chain: auth → ACL → handler.\n\nPublic methods (skip auth): `Healthz`. Auth interceptor checks method name and passes through.\n\n## IPC Path (replacing Unix socket)\n\nAll modes use gRPC — no Unix socket fallback. One code path, local and remote.\n\n```\nLocal:   Container → yao-grpc → Yao gRPC 127.0.0.1:9099\nRemote:  Container → yao-grpc → Tai :9100 relay → Yao gRPC :9099\n```\n\n`yao-grpc` reads `YAO_GRPC_ADDR` from env and connects. Local containers point directly at the Yao gRPC server on loopback; remote containers point at the Tai relay. No mode switch, no branching.\n\n### Tai relay routing\n\nTai does **not** know the Yao gRPC address at startup. yao-grpc tells Tai where to forward on every request via metadata:\n\n```\nManager.Create(sandbox)\n    ├─ oauth.MakeAccessToken(...)\n    ├─ oauth.MakeRefreshToken(...)\n    └─ tai.Client.Sandbox().Create(CreateRequest{\n           Env: {\n               YAO_TOKEN, YAO_REFRESH_TOKEN,\n               YAO_GRPC_ADDR: \"tai-host:9100\",\n               YAO_GRPC_UPSTREAM: \"yao-host:9099\",\n           },\n       })\n```\n\nyao-grpc reads `YAO_GRPC_UPSTREAM` from env and attaches it as `x-grpc-upstream` metadata on every request to Tai. Tai gateway reads this metadata and forwards to the specified address. No per-container state in Tai, no lookup table — pure transparent proxy. One Tai can serve containers from different Yao instances because each request carries its own target.\n\nFor local mode, no Tai relay — Manager injects `YAO_GRPC_ADDR=127.0.0.1:9099` directly (no `YAO_GRPC_UPSTREAM` needed).\n\n### yao-grpc (container client)\n\n`yao-grpc` is the in-container gRPC client binary. Replaces the old `yao-bridge`. Lives in `yao/tai/grpc/`:\n\n```\nyao/tai/grpc/\n├── grpc.go             // gRPC client: connect, forward MCP/process calls\n├── auth.go             // token management: read env, auto-refresh\n├── grpc_test.go\n└── cmd/\n    └── main.go\n```\n\nRationale for placing in `yao/tai`:\n- Consumes Tai relay — same layer as `tai/proxy`, `tai/volume`\n- Shares gRPC deps already in `yao/tai`\n- Version-locked with Tai SDK and server protocol\n- Built in same CI: `go build -o yao-grpc ./tai/grpc/cmd`\n\nPure client — no signing keys, no `oauth` package dependency. Reads `YAO_TOKEN` + `YAO_REFRESH_TOKEN` + `YAO_SANDBOX_ID` from env, attaches all three as gRPC metadata on every call. Token refresh is transparent — server auto-rotates expired tokens (same logic as HTTP guard) and returns new tokens via response metadata.\n\n## Proto\n\n```protobuf\nservice Yao {\n  // Base\n  rpc Run(RunRequest) returns (RunResponse);\n  rpc Stream(RunRequest) returns (stream Chunk);\n  rpc Shell(ShellRequest) returns (ShellResponse);\n  rpc ShellStream(ShellRequest) returns (stream Chunk);\n\n  // API gateway\n  rpc API(APIRequest) returns (APIResponse);\n\n  // MCP\n  rpc MCPListTools(MCPListRequest) returns (MCPListResponse);\n  rpc MCPCallTool(MCPCallRequest) returns (MCPCallResponse);\n  rpc MCPListResources(MCPListRequest) returns (MCPResourcesResponse);\n  rpc MCPReadResource(MCPResourceRequest) returns (MCPResourceResponse);\n\n  // AI - LLM\n  rpc ChatCompletions(ChatRequest) returns (ChatResponse);\n  rpc ChatCompletionsStream(ChatRequest) returns (stream ChatChunk);\n\n  // AI - Agent\n  rpc AgentStream(AgentRequest) returns (stream AgentChunk);\n\n  // Health\n  rpc Healthz(Empty) returns (HealthzResponse);\n}\n```\n\n### LLM layer\n\n`ChatCompletions` and `ChatCompletionsStream` call the existing `llm.ChatCompletions` process (`agent/llm/process.go`). It auto-detects connector type (openai/anthropic/etc.), selects the appropriate provider, and returns OpenAI-compatible format.\n\n```\ngRPC ChatCompletions(connector, messages, opts)\n    → process.Exec(\"llm.ChatCompletions\", connector, messages, opts)\n    → agent/llm.New(conn, opts) → provider.Stream/Post → response\n\ngRPC ChatCompletionsStream(connector, messages, opts)\n    → same path, with streaming callback → gRPC stream chunks\n```\n\nThe caller specifies a connector ID. The `llm.ChatCompletions` process resolves it via `connector.Select()`, creates the LLM instance, and executes. Streaming version passes a callback that forwards chunks to the gRPC stream.\n\n### Agent layer\n\n`AgentStream` wraps `agent/robots/:id/completions` — resolves robot → host assistant → runs agent pipeline → streams output. Only stream method — agent output is inherently streamed; non-stream callers simply consume all chunks. Internally calls `assistant.Stream()` with `ctx.Writer` set to nil (or noop) when the caller doesn't need incremental output.\n\n```\ngRPC AgentStream(agent_id, messages) → resolve robot → assistant.Stream() → stream chunks\n```\n\nThis enables container-internal agents to call other agents without HTTP, and remote `yao` instances to orchestrate agent pipelines cross-node.\n\n`AgentChunk` carries `agent/output/message.Message` — the same DSL used by HTTP SSE streaming. Each chunk is one JSON-serialized `Message`:\n\n```protobuf\nmessage AgentChunk {\n  bytes data = 1;  // JSON-encoded agent/output/message.Message\n  bool  done = 2;\n}\n```\n\nThe `Message` structure uses `Type` + `Props` to express all content types (text, thinking, tool_call, error, action, event, image, audio, video). Streaming control fields (`chunk_id`, `message_id`, `block_id`, `thread_id`) and delta fields (`delta`, `delta_path`, `delta_action`) are preserved as-is over gRPC — the client merges chunks using the same logic as CUI's SSE consumer.\n\n### Shell execution context\n\n`Shell` and `ShellStream` execute commands in the **Yao host process**, not inside a sandbox container. This is by design — the scope `grpc:shell` is a privileged capability, not granted to container tokens by default. Container-internal commands run via `tai.Client.Sandbox().Exec()`, which is a different path (not exposed as a gRPC method).\n\nSee [pb/yao.proto](./pb/yao.proto) for full message definitions.\n\n## Process & Stream (gou foundation)\n\ngRPC `Run` and `Stream` map to two parallel systems in `gou`:\n\n```\ngou/process/   — execute once, return result     → gRPC Run\ngou/stream/    — execute once, push chunks        → gRPC Stream\n```\n\n### gou/process (existing, unchanged)\n\n```go\ntype Handler func(process *Process) interface{}\n\nprocess.Register(\"scripts\", handler)\np := process.New(\"scripts.foo.bar\", args...)\np.Execute()\nresult := p.Value()\n```\n\n### gou/stream (new package, parallel to process)\n\n```go\ntype Handler func(ctx context.Context, process *Process, send func([]byte) error) error\n\nstream.Register(\"scripts\", handler)\ns := stream.New(\"scripts.foo.bar\", args...)\ns.Execute(ctx, func(chunk []byte) error { ... })\n```\n\n`stream.Process` mirrors `process.Process` fields (Name, Group, Method, ID, Args, Global, Sid, Authorized) but `ctx` is a first-class parameter, not buried in a struct field.\n\n`send` returns error when the receiver disconnects — handler should stop.\n\n### Fallback\n\nIf a stream handler is not registered for a name but a process handler exists, `stream.Execute` falls back to: run the process handler once, JSON-marshal the result, call `send` once.\n\n### Registration\n\n```go\n// gou/process — existing\nprocess.Register(\"models\", modelsHandler)\nprocess.Register(\"scripts\", scriptsHandler)\n\n// gou/stream — new, same namespace\nstream.Register(\"scripts\", scriptsStreamHandler)\nstream.Register(\"llm\", llmStreamHandler)\n```\n\nSame naming convention. A process name can have both a process handler and a stream handler.\n\n### gRPC mapping\n\n```go\nfunc (s *yaoServer) Run(ctx context.Context, req *pb.RunRequest) (*pb.RunResponse, error) {\n    p := process.NewWithContext(ctx, req.Process, args...)\n    if err := p.Execute(); err != nil { return nil, err }\n    data, _ := json.Marshal(p.Value())\n    return &pb.RunResponse{Result: data}, nil\n}\n\nfunc (s *yaoServer) Stream(req *pb.RunRequest, grpcStream pb.Yao_StreamServer) error {\n    st := stream.New(req.Process, args...)\n    return st.Execute(grpcStream.Context(), func(chunk []byte) error {\n        return grpcStream.Send(&pb.Chunk{Data: chunk})\n    })\n}\n```\n\n### V8 integration\n\nBoth are exposed as top-level globals in JavaScript, parallel:\n\n```go\n// gou/runtime/v8/isolate.go MakeTemplate\ntemplate.Set(\"Process\", processModule.ExportFunction(iso))        // existing\ntemplate.Set(\"Stream\",  streamModule.ExportFunction(iso))         // new\n```\n\n**JS calling Go stream** (JS is consumer):\n\n```javascript\nStream(\"llm.chat.completions\", function(chunk) {\n    log.Info(chunk)\n    return 1  // 1=continue, 0=stop\n}, { model: \"gpt-4\", messages: [...] })\n```\n\n**JS script as stream handler** (JS is producer):\n\n```javascript\n// scripts/report.js — registered via stream.Register(\"scripts\", ...)\nfunction generate(args, send) {\n    send(\"part 1\")\n    send(\"part 2\")\n}\n```\n\nV8 runtime registers both:\n\n```go\nfunc init() {\n    process.Register(\"scripts\", processScripts)   // existing\n    stream.Register(\"scripts\", processScriptsStream) // new\n}\n```\n\n`processScriptsStream` calls `script.ExecStream(ctx, p, send)` which injects `send` into the V8 global before executing the script method.\n\n### Impact on existing code\n\n| Component | Changes |\n|-----------|---------|\n| `gou/process/` | None |\n| `gou/stream/` | New package (~150 lines) |\n| `gou/runtime/v8/process.go` | +1 line: `stream.Register(...)` |\n| `gou/runtime/v8/script.go` | +`ExecStream` method |\n| `gou/runtime/v8/isolate.go` | +1 line: `template.Set(\"Stream\", ...)` |\n| `gou/runtime/v8/functions/` | +`stream/` module for JS→Go stream consumption |\n","workspace/DESIGN.md":"# workspace - UI Design System & Style Tokens\n\nPath: `workspace/DESIGN.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/workspace/DESIGN.md)","tools/secret/connectors_schema.json":"# Secret Skill\n\nPath: `tools/secret/connectors_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/secret/connectors_schema.json)","tools/secret/list_schema.json":"# Secret Skill\n\nPath: `tools/secret/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/secret/list_schema.json)","tools/secret/read_schema.json":"# Secret Skill\n\nPath: `tools/secret/read_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/secret/read_schema.json)","tools/robot/create_schema.json":"# Robot Skill\n\nPath: `tools/robot/create_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/create_schema.json)","tools/robot/execution_cancel_schema.json":"# Robot Skill\n\nPath: `tools/robot/execution_cancel_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/execution_cancel_schema.json)","tools/robot/execution_create_schema.json":"# Robot Skill\n\nPath: `tools/robot/execution_create_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/execution_create_schema.json)","tools/robot/execution_get_schema.json":"# Robot Skill\n\nPath: `tools/robot/execution_get_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/execution_get_schema.json)","tools/robot/execution_list_schema.json":"# Robot Skill\n\nPath: `tools/robot/execution_list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/execution_list_schema.json)","tools/robot/get_schema.json":"# Robot Skill\n\nPath: `tools/robot/get_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/get_schema.json)","tools/robot/list_schema.json":"# Robot Skill\n\nPath: `tools/robot/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/list_schema.json)","tools/robot/result_list_schema.json":"# Robot Skill\n\nPath: `tools/robot/result_list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/result_list_schema.json)","tools/robot/status_schema.json":"# Robot Skill\n\nPath: `tools/robot/status_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/status_schema.json)","tools/robot/update_schema.json":"# Robot Skill\n\nPath: `tools/robot/update_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/update_schema.json)","tools/proc/allowed.json":"# Proc Skill\n\nPath: `tools/proc/allowed.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/proc/allowed.json)","tools/proc/schema.json":"# Proc Skill\n\nPath: `tools/proc/schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/proc/schema.json)","tools/mobile/exec_schema.json":"# Mobile Skill\n\nPath: `tools/mobile/exec_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/exec_schema.json)","tools/mobile/info_schema.json":"# Mobile Skill\n\nPath: `tools/mobile/info_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/info_schema.json)","tools/mobile/list_schema.json":"# Mobile Skill\n\nPath: `tools/mobile/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/list_schema.json)","tools/mobile/pull_schema.json":"# Mobile Skill\n\nPath: `tools/mobile/pull_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/pull_schema.json)","tools/mobile/push_schema.json":"# Mobile Skill\n\nPath: `tools/mobile/push_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/push_schema.json)","tools/mobile/screenshot_schema.json":"# Mobile Skill\n\nPath: `tools/mobile/screenshot_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/screenshot_schema.json)","tools/mcps/agent.json":"# Mcps Skill\n\nPath: `tools/mcps/agent.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/agent.json)","tools/mcps/audio.json":"# Mcps Skill\n\nPath: `tools/mcps/audio.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/audio.json)","tools/mcps/clip.json":"# Mcps Skill\n\nPath: `tools/mcps/clip.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/clip.json)","tools/mcps/doc.json":"# Mcps Skill\n\nPath: `tools/mcps/doc.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/doc.json)","tools/mcps/image.json":"# Mcps Skill\n\nPath: `tools/mcps/image.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/image.json)","tools/mcps/kanban.json":"# Mcps Skill\n\nPath: `tools/mcps/kanban.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/kanban.json)","tools/mcps/mobile.json":"# Mcps Skill\n\nPath: `tools/mcps/mobile.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/mobile.json)","tools/mcps/process.json":"# Mcps Skill\n\nPath: `tools/mcps/process.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/process.json)","tools/mcps/robot.json":"# Mcps Skill\n\nPath: `tools/mcps/robot.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/robot.json)","tools/mcps/secret.json":"# Mcps Skill\n\nPath: `tools/mcps/secret.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/secret.json)","tools/mcps/web.json":"# Mcps Skill\n\nPath: `tools/mcps/web.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/web.json)","tools/mcps/workspace.json":"# Mcps Skill\n\nPath: `tools/mcps/workspace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/workspace.json)","tools/mcps/workspace-config.json":"# Mcps Skill\n\nPath: `tools/mcps/workspace-config.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/workspace-config.json)","tools/inbox/list_schema.json":"# Inbox Skill\n\nPath: `tools/inbox/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/inbox/list_schema.json)","tools/inbox/view_schema.json":"# Inbox Skill\n\nPath: `tools/inbox/view_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/inbox/view_schema.json)","tools/image/edit_schema.json":"# Image Skill\n\nPath: `tools/image/edit_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/edit_schema.json)","tools/image/generate_schema.json":"# Image Skill\n\nPath: `tools/image/generate_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/generate_schema.json)","tools/image/providers_schema.json":"# Image Skill\n\nPath: `tools/image/providers_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/providers_schema.json)","tools/image/read_schema.json":"# Image Skill\n\nPath: `tools/image/read_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/read_schema.json)","tools/docs/inspect.json":"# Docs Skill\n\nPath: `tools/docs/inspect.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/docs/inspect.json)","tools/docs/list.json":"# Docs Skill\n\nPath: `tools/docs/list.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/docs/list.json)","tools/docs/validate.json":"# Docs Skill\n\nPath: `tools/docs/validate.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/docs/validate.json)","tools/clip/list_schema.json":"# Clip Skill\n\nPath: `tools/clip/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/clip/list_schema.json)","tools/clip/read_schema.json":"# Clip Skill\n\nPath: `tools/clip/read_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/clip/read_schema.json)","tools/clip/write_schema.json":"# Clip Skill\n\nPath: `tools/clip/write_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/clip/write_schema.json)","tools/board/create_schema.json":"# Board Skill\n\nPath: `tools/board/create_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/board/create_schema.json)","tools/board/list_schema.json":"# Board Skill\n\nPath: `tools/board/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/board/list_schema.json)","tools/audio/providers_schema.json":"# Audio Skill\n\nPath: `tools/audio/providers_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/audio/providers_schema.json)","tools/audio/transcribe_schema.json":"# Audio Skill\n\nPath: `tools/audio/transcribe_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/audio/transcribe_schema.json)","tools/agents/a2a.md":"# Subagent: a2a\n\nPath: `tools/agents/a2a.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agents/a2a.md)","tools/agent/call_schema.json":"# Agent Skill\n\nPath: `tools/agent/call_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/call_schema.json)","tools/agent/connectors_schema.json":"# Agent Skill\n\nPath: `tools/agent/connectors_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/connectors_schema.json)","tools/agent/deploy_schema.json":"# Agent Skill\n\nPath: `tools/agent/deploy_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/deploy_schema.json)","tools/agent/download_schema.json":"# Agent Skill\n\nPath: `tools/agent/download_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/download_schema.json)","tools/agent/list_schema.json":"# Agent Skill\n\nPath: `tools/agent/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/list_schema.json)","tools/agent/reference_schema.json":"# Agent Skill\n\nPath: `tools/agent/reference_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/reference_schema.json)","tools/task/create_schema.json":"# Task Skill\n\nPath: `tools/task/create_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/create_schema.json)","tools/task/list_schema.json":"# Task Skill\n\nPath: `tools/task/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/list_schema.json)","tools/task/move_schema.json":"# Task Skill\n\nPath: `tools/task/move_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/move_schema.json)","tools/task/run_schema.json":"# Task Skill\n\nPath: `tools/task/run_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/run_schema.json)","tools/task/stop_schema.json":"# Task Skill\n\nPath: `tools/task/stop_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/stop_schema.json)","tools/webfetch/schema.json":"# Webfetch Skill\n\nPath: `tools/webfetch/schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/webfetch/schema.json)","tools/websearch/schema.json":"# Websearch Skill\n\nPath: `tools/websearch/schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/websearch/schema.json)","tools/workspace/file_list_schema.json":"# Workspace Skill\n\nPath: `tools/workspace/file_list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/file_list_schema.json)","tools/workspace/file_read_schema.json":"# Workspace Skill\n\nPath: `tools/workspace/file_read_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/file_read_schema.json)","tools/workspace/file_write_schema.json":"# Workspace Skill\n\nPath: `tools/workspace/file_write_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/file_write_schema.json)","tools/workspace/get_schema.json":"# Workspace Skill\n\nPath: `tools/workspace/get_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/get_schema.json)","tools/workspace/list_schema.json":"# Workspace Skill\n\nPath: `tools/workspace/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/list_schema.json)","tools/workspace_config/git_config_schema.json":"# Workspace_config Skill\n\nPath: `tools/workspace_config/git_config_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace_config/git_config_schema.json)","tools/workspace_config/git_credential_schema.json":"# Workspace_config Skill\n\nPath: `tools/workspace_config/git_credential_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace_config/git_credential_schema.json)","tools/workspace_config/ssh_key_schema.json":"# Workspace_config Skill\n\nPath: `tools/workspace_config/ssh_key_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace_config/ssh_key_schema.json)"},"items":[{"name":"DESIGN.md","path":"grpc/DESIGN.md","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/grpc/DESIGN.md","title":"grpc - UI Design System & Style Tokens","category":"project-spec","format":"markdown","content":"# Yao gRPC Server\n\nGeneral-purpose gRPC gateway for the Yao process. Shares OAuth + ACL scope system with openapi — one token, two protocols.\n\n## Services\n\n| Layer | Method | Purpose | Scope |\n|-------|--------|---------|-------|\n| **Base** | `Run` | Execute Yao process, return result | `grpc:run` |\n| | `Stream` | Execute Yao process, stream output | `grpc:stream` |\n| | `Shell` | Execute system command, wait for result | `grpc:shell` |\n| | `ShellStream` | Execute system command, stream stdout/stderr | `grpc:shell` |\n| **API** | `API` | Proxy to openapi, any endpoint | openapi's own scopes |\n| **MCP** | `MCPListTools` | List MCP tools for a session | `grpc:mcp` |\n| | `MCPCallTool` | Call MCP tool → process.Exec() | `grpc:mcp` |\n| | `MCPListResources` | List MCP resources | `grpc:mcp` |\n| | `MCPReadResource` | Read MCP resource | `grpc:mcp` |\n| **LLM** | `ChatCompletions` | Send messages to LLM, get response | `grpc:llm` |\n| | `ChatCompletionsStream` | Stream LLM response (SSE → gRPC stream) | `grpc:llm` |\n| **Agent** | `AgentStream` | Call agent, stream response | `grpc:agent` |\n\n## Clients\n\n- Container MCP tools (via Tai gRPC relay)\n- `yao run` CLI (after `yao login`)\n- Yao-to-Yao (cross-node process execution)\n\n## Auth\n\nSame as openapi. gRPC auth interceptor reuses the same `guard.Authenticate` logic — including automatic token refresh when access token is expired but refresh token is valid.\n\n```\nmetadata (Bearer + x-refresh-token)\n    → VerifyToken\n    → expired? → TryRefresh (same as guard.go) → new tokens in response metadata\n    → extract scopes → acl.Scope.Check(method, path, scopes)\n```\n\n### Infrastructure reuse assessment\n\nExisting openapi/oauth infrastructure can be reused for gRPC with **zero modifications**:\n\n| Component | Reusable as-is | Notes |\n|-----------|---------------|-------|\n| `VerifyToken(token string)` | Yes | Pure string input, no Gin dependency |\n| `MakeAccessToken(clientID, scope, subject, expiresIn, extraClaims...)` | Yes | Supports custom scope/subject for container tokens |\n| `MakeRefreshToken(...)` | Yes | Same as above |\n| `Revoke(ctx, token, tokenTypeHint)` | Yes | For container token cleanup on Remove |\n| `ScopeManager.Check(req *AccessRequest)` | Yes | Only needs `(Method, Path, Scopes)` — no Gin dependency |\n| `acl.Register(...)` | Yes | gRPC scopes registered via same pattern |\n\nThe `authorized.SetInfo` / `authorized.GetInfo` are Gin-bound but **not needed** — gRPC interceptor builds `AccessRequest` directly from JWT claims. Full `Enforce` chain (client/team/member) is HTTP multi-tenant only; gRPC uses `VerifyToken → ScopeManager.Check` which is sufficient.\n\nNew code required: ~80 lines (interceptor + scope registration). Existing code changes: **zero**.\n\n### CLI auth: `yao login` / `yao logout`\n\nOAuth 2.0 Device Authorization Grant. No `--remote` flag needed — logged in = gRPC, not logged in = local.\n\n```\n$ yao login --server https://yao.example.com\n请访问: https://yao.example.com/device\n输入代码: ABCD-1234\n等待授权... ✓ (token saved to ~/.yao/credentials)\n\n$ yao run models.user.Find '{\"id\":1}'    ← auto gRPC\n$ yao logout\n```\n\nRequires two new openapi endpoints:\n- `POST /oauth/device/authorize` — issue device_code + user_code\n- `POST /oauth/device/token` — poll for access_token\n\nToken scope: based on user's role, e.g. `grpc:run grpc:stream grpc:shell grpc:llm grpc:agent grpc:mcp`.\n\n**Implementation cost**: ~190 lines new code, ~10 lines changes to existing code.\nScaffolding already in place — `types.DeviceAuthorizationResponse`, `GrantTypeDeviceCode`, error codes (`ErrorAuthorizationPending`, `ErrorSlowDown`), `DeviceCodeLifetime` config, `DeviceAuthorization()` method signature, and HTTP route are all pre-defined. Core work:\n\n1. Implement `DeviceAuthorization()` in `device.go` (currently returns `nil, nil`)\n2. Add device_code store/get/consume helpers in `token.go`\n3. Add `GrantTypeDeviceCode` case to `Token()` switch in `core.go` (1 case branch)\n4. Implement `handleDeviceCodeGrant()` in `core.go`\n5. Add user authorization callback handler\n6. Fix discovery endpoint path inconsistency (`/oauth/device` vs `/oauth/device_authorization`)\n\nRisk: **very low** — all additions are in isolated code paths, no changes to existing `authorization_code` / `client_credentials` / `refresh_token` flows.\n\n### Container token\n\nContainer images and `yao-grpc` (`yao/tai/grpc/`) are ours — it handles token refresh automatically.\n\n```\nManager creates container\n    ├─ oauth.MakeAccessToken(subject=userID, scope=\"grpc:mcp grpc:run\")\n    ├─ oauth.MakeRefreshToken(...)\n    └─ tai.Client.Sandbox().Create(CreateRequest{\n           Env: {\n               YAO_TOKEN, YAO_REFRESH_TOKEN, YAO_SANDBOX_ID,\n               YAO_GRPC_ADDR,                 // where to connect\n               YAO_GRPC_UPSTREAM,             // remote only: where Tai should forward to\n           },\n       })\n\n       Local:  YAO_GRPC_ADDR=127.0.0.1:9099    (direct to Yao, no upstream needed)\n       Remote: YAO_GRPC_ADDR=tai-host:9100      YAO_GRPC_UPSTREAM=yao-host:9099\n\nyao-grpc (tai/grpc/, container-internal)\n    ├─ reads YAO_GRPC_ADDR + YAO_TOKEN + YAO_REFRESH_TOKEN + YAO_SANDBOX_ID from env\n    ├─ if YAO_GRPC_UPSTREAM set: attaches x-grpc-upstream metadata (tells Tai where to forward)\n    ├─ every call: Bearer token + x-refresh-token + x-sandbox-id in gRPC metadata\n    ├─ server auth interceptor reuses guard.Authenticate logic:\n    │   token valid → pass through\n    │   token expired + refresh token present → auto rotate (same as HTTP guard)\n    │   new tokens returned via response metadata (x-access-token, x-refresh-token)\n    ├─ yao-grpc reads response metadata, updates tokens in memory\n    └─ transparent to caller, no separate refresh RPC needed\n```\n\n- access_token: short TTL (15m)\n- refresh_token: no expiry (valid until container removed)\n- Manager revokes refresh_token on container Remove\n- Tai does NOT know Yao address at startup — yao-grpc carries target in request metadata\n\n### Virtual endpoint mapping\n\n| gRPC | Virtual endpoint |\n|------|-----------------|\n| Run(\"models.user.Find\") | `POST /grpc/run/models.user.Find` |\n| Stream(\"flows.report\") | `POST /grpc/stream/flows.report` |\n| Shell | `POST /grpc/shell` |\n| ShellStream | `POST /grpc/shell` (same) |\n| API(POST, /kb/collections) | `POST /kb/collections` (real openapi path) |\n| MCPListTools | `GET /grpc/mcp/tools` |\n| MCPCallTool(\"search\") | `POST /grpc/mcp/call/search` |\n| MCPListResources | `GET /grpc/mcp/resources` |\n| MCPReadResource(\"uri\") | `GET /grpc/mcp/resources/read` |\n| ChatCompletions | `POST /grpc/llm/completions` |\n| ChatCompletionsStream | `POST /grpc/llm/completions` (same) |\n| AgentStream(\"robot-id\") | `POST /grpc/agent/robot-id` |\n\nAPI method uses the **actual openapi path** — no virtual mapping needed, scope check is identical to HTTP.\n\n### Scope registration\n\n```go\nfunc init() {\n    acl.Register(\n        &acl.ScopeDefinition{Name: \"grpc:run\",    Endpoints: []string{\"POST /grpc/run/*\"}},\n        &acl.ScopeDefinition{Name: \"grpc:stream\", Endpoints: []string{\"POST /grpc/stream/*\"}},\n        &acl.ScopeDefinition{Name: \"grpc:shell\",  Endpoints: []string{\"POST /grpc/shell\"}},\n        &acl.ScopeDefinition{Name: \"grpc:mcp\",    Endpoints: []string{\"GET /grpc/mcp/tools\", \"POST /grpc/mcp/call/*\", \"GET /grpc/mcp/resources\", \"GET /grpc/mcp/resources/read\"}},\n        &acl.ScopeDefinition{Name: \"grpc:llm\",    Endpoints: []string{\"POST /grpc/llm/completions\"}},\n        &acl.ScopeDefinition{Name: \"grpc:agent\", Endpoints: []string{\"POST /grpc/agent/*\"}},\n    )\n}\n```\n\n## Network\n\n### Server listen config\n\n| Env | Default | Purpose |\n|-----|---------|---------|\n| `YAO_GRPC_HOST` | `127.0.0.1` | Comma-separated bind addresses. |\n| `YAO_GRPC_PORT` | `9099` | Listen port (shared by all addresses). |\n| `YAO_GRPC` | _(unset)_ | Set `off` to explicitly disable gRPC server. |\n\ngRPC server **defaults to enabled** (`127.0.0.1:9099`) — sandbox container callbacks depend on it.\n\n`YAO_GRPC_HOST` accepts one or more addresses separated by `,`. Each address gets its own `net.Listener`; all listeners feed into the same `grpc.Server` (gRPC supports multiple `Serve` calls on one server).\n\n| Scenario | Config | Effect |\n|----------|--------|--------|\n| Local dev / default | _(nothing to set)_ | `127.0.0.1:9099` — loopback, sandbox works out of box |\n| LAN multi-NIC | `YAO_GRPC_HOST=192.168.10.1,10.0.0.1` | Binds each internal IP |\n| Open | `YAO_GRPC_HOST=0.0.0.0` | All interfaces |\n| Disabled | `YAO_GRPC=off` | gRPC server not started (pure API gateway, no sandbox) |\n\nWhen multiple addresses are given, the server creates one goroutine per listener. Shutdown (`grpc.GracefulStop`) drains all listeners.\n\nConfig lives in `config.Config.GRPC` (type `GRPCConfig`), same pattern as `Host`/`Port` for HTTP.\n\n### Startup\n\ngRPC server starts **after** HTTP server in `cmd/start.go`, as a parallel goroutine:\n\n```\nengine.Load → itask.Start → ischedule.Start → service.Start (HTTP) → grpc.StartServer (gRPC)\n```\n\ngRPC server starts by default. Set `YAO_GRPC=off` to explicitly disable (no-op startup). Any other value or unset means enabled.\n\nShutdown: `defer grpc.Stop()` in `cmd/start.go`, called before HTTP stop for graceful drain.\n\n### Access control\n\nLocal: containers and CLI connect via loopback. Remote: only Tai relay connects (address known from `YAO_TAI_ADDR`). All callers carry OAuth tokens — no IP allowlist needed.\n\nInterceptor chain: auth → ACL → handler.\n\nPublic methods (skip auth): `Healthz`. Auth interceptor checks method name and passes through.\n\n## IPC Path (replacing Unix socket)\n\nAll modes use gRPC — no Unix socket fallback. One code path, local and remote.\n\n```\nLocal:   Container → yao-grpc → Yao gRPC 127.0.0.1:9099\nRemote:  Container → yao-grpc → Tai :9100 relay → Yao gRPC :9099\n```\n\n`yao-grpc` reads `YAO_GRPC_ADDR` from env and connects. Local containers point directly at the Yao gRPC server on loopback; remote containers point at the Tai relay. No mode switch, no branching.\n\n### Tai relay routing\n\nTai does **not** know the Yao gRPC address at startup. yao-grpc tells Tai where to forward on every request via metadata:\n\n```\nManager.Create(sandbox)\n    ├─ oauth.MakeAccessToken(...)\n    ├─ oauth.MakeRefreshToken(...)\n    └─ tai.Client.Sandbox().Create(CreateRequest{\n           Env: {\n               YAO_TOKEN, YAO_REFRESH_TOKEN,\n               YAO_GRPC_ADDR: \"tai-host:9100\",\n               YAO_GRPC_UPSTREAM: \"yao-host:9099\",\n           },\n       })\n```\n\nyao-grpc reads `YAO_GRPC_UPSTREAM` from env and attaches it as `x-grpc-upstream` metadata on every request to Tai. Tai gateway reads this metadata and forwards to the specified address. No per-container state in Tai, no lookup table — pure transparent proxy. One Tai can serve containers from different Yao instances because each request carries its own target.\n\nFor local mode, no Tai relay — Manager injects `YAO_GRPC_ADDR=127.0.0.1:9099` directly (no `YAO_GRPC_UPSTREAM` needed).\n\n### yao-grpc (container client)\n\n`yao-grpc` is the in-container gRPC client binary. Replaces the old `yao-bridge`. Lives in `yao/tai/grpc/`:\n\n```\nyao/tai/grpc/\n├── grpc.go             // gRPC client: connect, forward MCP/process calls\n├── auth.go             // token management: read env, auto-refresh\n├── grpc_test.go\n└── cmd/\n    └── main.go\n```\n\nRationale for placing in `yao/tai`:\n- Consumes Tai relay — same layer as `tai/proxy`, `tai/volume`\n- Shares gRPC deps already in `yao/tai`\n- Version-locked with Tai SDK and server protocol\n- Built in same CI: `go build -o yao-grpc ./tai/grpc/cmd`\n\nPure client — no signing keys, no `oauth` package dependency. Reads `YAO_TOKEN` + `YAO_REFRESH_TOKEN` + `YAO_SANDBOX_ID` from env, attaches all three as gRPC metadata on every call. Token refresh is transparent — server auto-rotates expired tokens (same logic as HTTP guard) and returns new tokens via response metadata.\n\n## Proto\n\n```protobuf\nservice Yao {\n  // Base\n  rpc Run(RunRequest) returns (RunResponse);\n  rpc Stream(RunRequest) returns (stream Chunk);\n  rpc Shell(ShellRequest) returns (ShellResponse);\n  rpc ShellStream(ShellRequest) returns (stream Chunk);\n\n  // API gateway\n  rpc API(APIRequest) returns (APIResponse);\n\n  // MCP\n  rpc MCPListTools(MCPListRequest) returns (MCPListResponse);\n  rpc MCPCallTool(MCPCallRequest) returns (MCPCallResponse);\n  rpc MCPListResources(MCPListRequest) returns (MCPResourcesResponse);\n  rpc MCPReadResource(MCPResourceRequest) returns (MCPResourceResponse);\n\n  // AI - LLM\n  rpc ChatCompletions(ChatRequest) returns (ChatResponse);\n  rpc ChatCompletionsStream(ChatRequest) returns (stream ChatChunk);\n\n  // AI - Agent\n  rpc AgentStream(AgentRequest) returns (stream AgentChunk);\n\n  // Health\n  rpc Healthz(Empty) returns (HealthzResponse);\n}\n```\n\n### LLM layer\n\n`ChatCompletions` and `ChatCompletionsStream` call the existing `llm.ChatCompletions` process (`agent/llm/process.go`). It auto-detects connector type (openai/anthropic/etc.), selects the appropriate provider, and returns OpenAI-compatible format.\n\n```\ngRPC ChatCompletions(connector, messages, opts)\n    → process.Exec(\"llm.ChatCompletions\", connector, messages, opts)\n    → agent/llm.New(conn, opts) → provider.Stream/Post → response\n\ngRPC ChatCompletionsStream(connector, messages, opts)\n    → same path, with streaming callback → gRPC stream chunks\n```\n\nThe caller specifies a connector ID. The `llm.ChatCompletions` process resolves it via `connector.Select()`, creates the LLM instance, and executes. Streaming version passes a callback that forwards chunks to the gRPC stream.\n\n### Agent layer\n\n`AgentStream` wraps `agent/robots/:id/completions` — resolves robot → host assistant → runs agent pipeline → streams output. Only stream method — agent output is inherently streamed; non-stream callers simply consume all chunks. Internally calls `assistant.Stream()` with `ctx.Writer` set to nil (or noop) when the caller doesn't need incremental output.\n\n```\ngRPC AgentStream(agent_id, messages) → resolve robot → assistant.Stream() → stream chunks\n```\n\nThis enables container-internal agents to call other agents without HTTP, and remote `yao` instances to orchestrate agent pipelines cross-node.\n\n`AgentChunk` carries `agent/output/message.Message` — the same DSL used by HTTP SSE streaming. Each chunk is one JSON-serialized `Message`:\n\n```protobuf\nmessage AgentChunk {\n  bytes data = 1;  // JSON-encoded agent/output/message.Message\n  bool  done = 2;\n}\n```\n\nThe `Message` structure uses `Type` + `Props` to express all content types (text, thinking, tool_call, error, action, event, image, audio, video). Streaming control fields (`chunk_id`, `message_id`, `block_id`, `thread_id`) and delta fields (`delta`, `delta_path`, `delta_action`) are preserved as-is over gRPC — the client merges chunks using the same logic as CUI's SSE consumer.\n\n### Shell execution context\n\n`Shell` and `ShellStream` execute commands in the **Yao host process**, not inside a sandbox container. This is by design — the scope `grpc:shell` is a privileged capability, not granted to container tokens by default. Container-internal commands run via `tai.Client.Sandbox().Exec()`, which is a different path (not exposed as a gRPC method).\n\nSee [pb/yao.proto](./pb/yao.proto) for full message definitions.\n\n## Process & Stream (gou foundation)\n\ngRPC `Run` and `Stream` map to two parallel systems in `gou`:\n\n```\ngou/process/   — execute once, return result     → gRPC Run\ngou/stream/    — execute once, push chunks        → gRPC Stream\n```\n\n### gou/process (existing, unchanged)\n\n```go\ntype Handler func(process *Process) interface{}\n\nprocess.Register(\"scripts\", handler)\np := process.New(\"scripts.foo.bar\", args...)\np.Execute()\nresult := p.Value()\n```\n\n### gou/stream (new package, parallel to process)\n\n```go\ntype Handler func(ctx context.Context, process *Process, send func([]byte) error) error\n\nstream.Register(\"scripts\", handler)\ns := stream.New(\"scripts.foo.bar\", args...)\ns.Execute(ctx, func(chunk []byte) error { ... })\n```\n\n`stream.Process` mirrors `process.Process` fields (Name, Group, Method, ID, Args, Global, Sid, Authorized) but `ctx` is a first-class parameter, not buried in a struct field.\n\n`send` returns error when the receiver disconnects — handler should stop.\n\n### Fallback\n\nIf a stream handler is not registered for a name but a process handler exists, `stream.Execute` falls back to: run the process handler once, JSON-marshal the result, call `send` once.\n\n### Registration\n\n```go\n// gou/process — existing\nprocess.Register(\"models\", modelsHandler)\nprocess.Register(\"scripts\", scriptsHandler)\n\n// gou/stream — new, same namespace\nstream.Register(\"scripts\", scriptsStreamHandler)\nstream.Register(\"llm\", llmStreamHandler)\n```\n\nSame naming convention. A process name can have both a process handler and a stream handler.\n\n### gRPC mapping\n\n```go\nfunc (s *yaoServer) Run(ctx context.Context, req *pb.RunRequest) (*pb.RunResponse, error) {\n    p := process.NewWithContext(ctx, req.Process, args...)\n    if err := p.Execute(); err != nil { return nil, err }\n    data, _ := json.Marshal(p.Value())\n    return &pb.RunResponse{Result: data}, nil\n}\n\nfunc (s *yaoServer) Stream(req *pb.RunRequest, grpcStream pb.Yao_StreamServer) error {\n    st := stream.New(req.Process, args...)\n    return st.Execute(grpcStream.Context(), func(chunk []byte) error {\n        return grpcStream.Send(&pb.Chunk{Data: chunk})\n    })\n}\n```\n\n### V8 integration\n\nBoth are exposed as top-level globals in JavaScript, parallel:\n\n```go\n// gou/runtime/v8/isolate.go MakeTemplate\ntemplate.Set(\"Process\", processModule.ExportFunction(iso))        // existing\ntemplate.Set(\"Stream\",  streamModule.ExportFunction(iso))         // new\n```\n\n**JS calling Go stream** (JS is consumer):\n\n```javascript\nStream(\"llm.chat.completions\", function(chunk) {\n    log.Info(chunk)\n    return 1  // 1=continue, 0=stop\n}, { model: \"gpt-4\", messages: [...] })\n```\n\n**JS script as stream handler** (JS is producer):\n\n```javascript\n// scripts/report.js — registered via stream.Register(\"scripts\", ...)\nfunction generate(args, send) {\n    send(\"part 1\")\n    send(\"part 2\")\n}\n```\n\nV8 runtime registers both:\n\n```go\nfunc init() {\n    process.Register(\"scripts\", processScripts)   // existing\n    stream.Register(\"scripts\", processScriptsStream) // new\n}\n```\n\n`processScriptsStream` calls `script.ExecStream(ctx, p, send)` which injects `send` into the V8 global before executing the script method.\n\n### Impact on existing code\n\n| Component | Changes |\n|-----------|---------|\n| `gou/process/` | None |\n| `gou/stream/` | New package (~150 lines) |\n| `gou/runtime/v8/process.go` | +1 line: `stream.Register(...)` |\n| `gou/runtime/v8/script.go` | +`ExecStream` method |\n| `gou/runtime/v8/isolate.go` | +1 line: `template.Set(\"Stream\", ...)` |\n| `gou/runtime/v8/functions/` | +`stream/` module for JS→Go stream consumption |\n","isInternal":false,"tokens":4886,"sizeBytes":19069},{"name":"DESIGN.md","path":"workspace/DESIGN.md","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/workspace/DESIGN.md","title":"workspace - UI Design System & Style Tokens","category":"project-spec","format":"markdown","content":"# workspace - UI Design System & Style Tokens\n\nPath: `workspace/DESIGN.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/workspace/DESIGN.md)","tokens":300,"sizeBytes":175},{"name":"call_schema.json","path":"tools/agent/call_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/call_schema.json","title":"Agent Skill","category":"anthropic-skill","format":"json","content":"# Agent Skill\n\nPath: `tools/agent/call_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/call_schema.json)","tokens":300,"sizeBytes":161},{"name":"connectors_schema.json","path":"tools/agent/connectors_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/connectors_schema.json","title":"Agent Skill","category":"anthropic-skill","format":"json","content":"# Agent Skill\n\nPath: `tools/agent/connectors_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/connectors_schema.json)","tokens":300,"sizeBytes":173},{"name":"deploy_schema.json","path":"tools/agent/deploy_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/deploy_schema.json","title":"Agent Skill","category":"anthropic-skill","format":"json","content":"# Agent Skill\n\nPath: `tools/agent/deploy_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/deploy_schema.json)","tokens":300,"sizeBytes":165},{"name":"download_schema.json","path":"tools/agent/download_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/download_schema.json","title":"Agent Skill","category":"anthropic-skill","format":"json","content":"# Agent Skill\n\nPath: `tools/agent/download_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/download_schema.json)","tokens":300,"sizeBytes":169},{"name":"list_schema.json","path":"tools/agent/list_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/list_schema.json","title":"Agent Skill","category":"anthropic-skill","format":"json","content":"# Agent Skill\n\nPath: `tools/agent/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/list_schema.json)","tokens":300,"sizeBytes":161},{"name":"reference_schema.json","path":"tools/agent/reference_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/reference_schema.json","title":"Agent Skill","category":"anthropic-skill","format":"json","content":"# Agent Skill\n\nPath: `tools/agent/reference_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/reference_schema.json)","tokens":300,"sizeBytes":171},{"name":"providers_schema.json","path":"tools/audio/providers_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/audio/providers_schema.json","title":"Audio Skill","category":"anthropic-skill","format":"json","content":"# Audio Skill\n\nPath: `tools/audio/providers_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/audio/providers_schema.json)","tokens":300,"sizeBytes":171},{"name":"transcribe_schema.json","path":"tools/audio/transcribe_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/audio/transcribe_schema.json","title":"Audio Skill","category":"anthropic-skill","format":"json","content":"# Audio Skill\n\nPath: `tools/audio/transcribe_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/audio/transcribe_schema.json)","tokens":300,"sizeBytes":173},{"name":"create_schema.json","path":"tools/board/create_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/board/create_schema.json","title":"Board Skill","category":"anthropic-skill","format":"json","content":"# Board Skill\n\nPath: `tools/board/create_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/board/create_schema.json)","tokens":300,"sizeBytes":165},{"name":"list_schema.json","path":"tools/board/list_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/board/list_schema.json","title":"Board Skill","category":"anthropic-skill","format":"json","content":"# Board Skill\n\nPath: `tools/board/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/board/list_schema.json)","tokens":300,"sizeBytes":161},{"name":"list_schema.json","path":"tools/clip/list_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/clip/list_schema.json","title":"Clip Skill","category":"anthropic-skill","format":"json","content":"# Clip Skill\n\nPath: `tools/clip/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/clip/list_schema.json)","tokens":300,"sizeBytes":158},{"name":"read_schema.json","path":"tools/clip/read_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/clip/read_schema.json","title":"Clip Skill","category":"anthropic-skill","format":"json","content":"# Clip Skill\n\nPath: `tools/clip/read_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/clip/read_schema.json)","tokens":300,"sizeBytes":158},{"name":"write_schema.json","path":"tools/clip/write_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/clip/write_schema.json","title":"Clip Skill","category":"anthropic-skill","format":"json","content":"# Clip Skill\n\nPath: `tools/clip/write_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/clip/write_schema.json)","tokens":300,"sizeBytes":160},{"name":"inspect.json","path":"tools/docs/inspect.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/docs/inspect.json","title":"Docs Skill","category":"anthropic-skill","format":"json","content":"# Docs Skill\n\nPath: `tools/docs/inspect.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/docs/inspect.json)","tokens":300,"sizeBytes":150},{"name":"list.json","path":"tools/docs/list.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/docs/list.json","title":"Docs Skill","category":"anthropic-skill","format":"json","content":"# Docs Skill\n\nPath: `tools/docs/list.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/docs/list.json)","tokens":300,"sizeBytes":144},{"name":"validate.json","path":"tools/docs/validate.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/docs/validate.json","title":"Docs Skill","category":"anthropic-skill","format":"json","content":"# Docs Skill\n\nPath: `tools/docs/validate.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/docs/validate.json)","tokens":300,"sizeBytes":152},{"name":"edit_schema.json","path":"tools/image/edit_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/edit_schema.json","title":"Image Skill","category":"anthropic-skill","format":"json","content":"# Image Skill\n\nPath: `tools/image/edit_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/edit_schema.json)","tokens":300,"sizeBytes":161},{"name":"generate_schema.json","path":"tools/image/generate_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/generate_schema.json","title":"Image Skill","category":"anthropic-skill","format":"json","content":"# Image Skill\n\nPath: `tools/image/generate_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/generate_schema.json)","tokens":300,"sizeBytes":169},{"name":"providers_schema.json","path":"tools/image/providers_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/providers_schema.json","title":"Image Skill","category":"anthropic-skill","format":"json","content":"# Image Skill\n\nPath: `tools/image/providers_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/providers_schema.json)","tokens":300,"sizeBytes":171},{"name":"read_schema.json","path":"tools/image/read_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/read_schema.json","title":"Image Skill","category":"anthropic-skill","format":"json","content":"# Image Skill\n\nPath: `tools/image/read_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/read_schema.json)","tokens":300,"sizeBytes":161},{"name":"list_schema.json","path":"tools/inbox/list_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/inbox/list_schema.json","title":"Inbox Skill","category":"anthropic-skill","format":"json","content":"# Inbox Skill\n\nPath: `tools/inbox/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/inbox/list_schema.json)","tokens":300,"sizeBytes":161},{"name":"view_schema.json","path":"tools/inbox/view_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/inbox/view_schema.json","title":"Inbox Skill","category":"anthropic-skill","format":"json","content":"# Inbox Skill\n\nPath: `tools/inbox/view_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/inbox/view_schema.json)","tokens":300,"sizeBytes":161},{"name":"agent.json","path":"tools/mcps/agent.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/agent.json","title":"Mcps Skill","category":"anthropic-skill","format":"json","content":"# Mcps Skill\n\nPath: `tools/mcps/agent.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/agent.json)","tokens":300,"sizeBytes":146},{"name":"audio.json","path":"tools/mcps/audio.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/audio.json","title":"Mcps Skill","category":"anthropic-skill","format":"json","content":"# Mcps Skill\n\nPath: `tools/mcps/audio.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/audio.json)","tokens":300,"sizeBytes":146},{"name":"clip.json","path":"tools/mcps/clip.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/clip.json","title":"Mcps Skill","category":"anthropic-skill","format":"json","content":"# Mcps Skill\n\nPath: `tools/mcps/clip.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/clip.json)","tokens":300,"sizeBytes":144},{"name":"doc.json","path":"tools/mcps/doc.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/doc.json","title":"Mcps Skill","category":"anthropic-skill","format":"json","content":"# Mcps Skill\n\nPath: `tools/mcps/doc.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/doc.json)","tokens":300,"sizeBytes":142},{"name":"image.json","path":"tools/mcps/image.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/image.json","title":"Mcps Skill","category":"anthropic-skill","format":"json","content":"# Mcps Skill\n\nPath: `tools/mcps/image.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/image.json)","tokens":300,"sizeBytes":146},{"name":"kanban.json","path":"tools/mcps/kanban.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/kanban.json","title":"Mcps Skill","category":"anthropic-skill","format":"json","content":"# Mcps Skill\n\nPath: `tools/mcps/kanban.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/kanban.json)","tokens":300,"sizeBytes":148},{"name":"mobile.json","path":"tools/mcps/mobile.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/mobile.json","title":"Mcps Skill","category":"anthropic-skill","format":"json","content":"# Mcps Skill\n\nPath: `tools/mcps/mobile.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/mobile.json)","tokens":300,"sizeBytes":148},{"name":"process.json","path":"tools/mcps/process.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/process.json","title":"Mcps Skill","category":"anthropic-skill","format":"json","content":"# Mcps Skill\n\nPath: `tools/mcps/process.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/process.json)","tokens":300,"sizeBytes":150},{"name":"robot.json","path":"tools/mcps/robot.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/robot.json","title":"Mcps Skill","category":"anthropic-skill","format":"json","content":"# Mcps Skill\n\nPath: `tools/mcps/robot.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/robot.json)","tokens":300,"sizeBytes":146},{"name":"secret.json","path":"tools/mcps/secret.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/secret.json","title":"Mcps Skill","category":"anthropic-skill","format":"json","content":"# Mcps Skill\n\nPath: `tools/mcps/secret.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/secret.json)","tokens":300,"sizeBytes":148},{"name":"web.json","path":"tools/mcps/web.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/web.json","title":"Mcps Skill","category":"anthropic-skill","format":"json","content":"# Mcps Skill\n\nPath: `tools/mcps/web.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/web.json)","tokens":300,"sizeBytes":142},{"name":"workspace-config.json","path":"tools/mcps/workspace-config.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/workspace-config.json","title":"Mcps Skill","category":"anthropic-skill","format":"json","content":"# Mcps Skill\n\nPath: `tools/mcps/workspace-config.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/workspace-config.json)","tokens":300,"sizeBytes":168},{"name":"workspace.json","path":"tools/mcps/workspace.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/workspace.json","title":"Mcps Skill","category":"anthropic-skill","format":"json","content":"# Mcps Skill\n\nPath: `tools/mcps/workspace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/workspace.json)","tokens":300,"sizeBytes":154},{"name":"exec_schema.json","path":"tools/mobile/exec_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/exec_schema.json","title":"Mobile Skill","category":"anthropic-skill","format":"json","content":"# Mobile Skill\n\nPath: `tools/mobile/exec_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/exec_schema.json)","tokens":300,"sizeBytes":164},{"name":"info_schema.json","path":"tools/mobile/info_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/info_schema.json","title":"Mobile Skill","category":"anthropic-skill","format":"json","content":"# Mobile Skill\n\nPath: `tools/mobile/info_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/info_schema.json)","tokens":300,"sizeBytes":164},{"name":"list_schema.json","path":"tools/mobile/list_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/list_schema.json","title":"Mobile Skill","category":"anthropic-skill","format":"json","content":"# Mobile Skill\n\nPath: `tools/mobile/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/list_schema.json)","tokens":300,"sizeBytes":164},{"name":"pull_schema.json","path":"tools/mobile/pull_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/pull_schema.json","title":"Mobile Skill","category":"anthropic-skill","format":"json","content":"# Mobile Skill\n\nPath: `tools/mobile/pull_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/pull_schema.json)","tokens":300,"sizeBytes":164},{"name":"push_schema.json","path":"tools/mobile/push_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/push_schema.json","title":"Mobile Skill","category":"anthropic-skill","format":"json","content":"# Mobile Skill\n\nPath: `tools/mobile/push_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/push_schema.json)","tokens":300,"sizeBytes":164},{"name":"screenshot_schema.json","path":"tools/mobile/screenshot_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/screenshot_schema.json","title":"Mobile Skill","category":"anthropic-skill","format":"json","content":"# Mobile Skill\n\nPath: `tools/mobile/screenshot_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mobile/screenshot_schema.json)","tokens":300,"sizeBytes":176},{"name":"allowed.json","path":"tools/proc/allowed.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/proc/allowed.json","title":"Proc Skill","category":"anthropic-skill","format":"json","content":"# Proc Skill\n\nPath: `tools/proc/allowed.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/proc/allowed.json)","tokens":300,"sizeBytes":150},{"name":"schema.json","path":"tools/proc/schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/proc/schema.json","title":"Proc Skill","category":"anthropic-skill","format":"json","content":"# Proc Skill\n\nPath: `tools/proc/schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/proc/schema.json)","tokens":300,"sizeBytes":148},{"name":"create_schema.json","path":"tools/robot/create_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/create_schema.json","title":"Robot Skill","category":"anthropic-skill","format":"json","content":"# Robot Skill\n\nPath: `tools/robot/create_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/create_schema.json)","tokens":300,"sizeBytes":165},{"name":"execution_cancel_schema.json","path":"tools/robot/execution_cancel_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/execution_cancel_schema.json","title":"Robot Skill","category":"anthropic-skill","format":"json","content":"# Robot Skill\n\nPath: `tools/robot/execution_cancel_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/execution_cancel_schema.json)","tokens":300,"sizeBytes":185},{"name":"execution_create_schema.json","path":"tools/robot/execution_create_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/execution_create_schema.json","title":"Robot Skill","category":"anthropic-skill","format":"json","content":"# Robot Skill\n\nPath: `tools/robot/execution_create_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/execution_create_schema.json)","tokens":300,"sizeBytes":185},{"name":"execution_get_schema.json","path":"tools/robot/execution_get_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/execution_get_schema.json","title":"Robot Skill","category":"anthropic-skill","format":"json","content":"# Robot Skill\n\nPath: `tools/robot/execution_get_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/execution_get_schema.json)","tokens":300,"sizeBytes":179},{"name":"execution_list_schema.json","path":"tools/robot/execution_list_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/execution_list_schema.json","title":"Robot Skill","category":"anthropic-skill","format":"json","content":"# Robot Skill\n\nPath: `tools/robot/execution_list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/execution_list_schema.json)","tokens":300,"sizeBytes":181},{"name":"get_schema.json","path":"tools/robot/get_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/get_schema.json","title":"Robot Skill","category":"anthropic-skill","format":"json","content":"# Robot Skill\n\nPath: `tools/robot/get_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/get_schema.json)","tokens":300,"sizeBytes":159},{"name":"list_schema.json","path":"tools/robot/list_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/list_schema.json","title":"Robot Skill","category":"anthropic-skill","format":"json","content":"# Robot Skill\n\nPath: `tools/robot/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/list_schema.json)","tokens":300,"sizeBytes":161},{"name":"result_list_schema.json","path":"tools/robot/result_list_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/result_list_schema.json","title":"Robot Skill","category":"anthropic-skill","format":"json","content":"# Robot Skill\n\nPath: `tools/robot/result_list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/result_list_schema.json)","tokens":300,"sizeBytes":175},{"name":"status_schema.json","path":"tools/robot/status_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/status_schema.json","title":"Robot Skill","category":"anthropic-skill","format":"json","content":"# Robot Skill\n\nPath: `tools/robot/status_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/status_schema.json)","tokens":300,"sizeBytes":165},{"name":"update_schema.json","path":"tools/robot/update_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/update_schema.json","title":"Robot Skill","category":"anthropic-skill","format":"json","content":"# Robot Skill\n\nPath: `tools/robot/update_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/robot/update_schema.json)","tokens":300,"sizeBytes":165},{"name":"connectors_schema.json","path":"tools/secret/connectors_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/secret/connectors_schema.json","title":"Secret Skill","category":"anthropic-skill","format":"json","content":"# Secret Skill\n\nPath: `tools/secret/connectors_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/secret/connectors_schema.json)","tokens":300,"sizeBytes":176},{"name":"list_schema.json","path":"tools/secret/list_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/secret/list_schema.json","title":"Secret Skill","category":"anthropic-skill","format":"json","content":"# Secret Skill\n\nPath: `tools/secret/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/secret/list_schema.json)","tokens":300,"sizeBytes":164},{"name":"read_schema.json","path":"tools/secret/read_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/secret/read_schema.json","title":"Secret Skill","category":"anthropic-skill","format":"json","content":"# Secret Skill\n\nPath: `tools/secret/read_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/secret/read_schema.json)","tokens":300,"sizeBytes":164},{"name":"SKILL.md","path":"tools/secret/SKILL.md","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/secret/SKILL.md","title":"secret","category":"anthropic-skill","format":"markdown","content":"# Secret Management\n\nAgents can read user-configured secrets at runtime using the `tai tool` CLI.\nSecrets are encrypted at rest (AES-256-GCM) and decrypted only when read.\n\n## Available Tools\n\n| Tool | Description |\n|------|-------------|\n| `secret_list` | List secret names and descriptions (no values) |\n| `secret_read` | Read a single secret value by name |\n\n## Usage\n\n### Bash\n\n```bash\n# List available secrets\ntai tool secret_list\n\n# Read a secret\nTOKEN=$(tai tool secret_read '{\"name\": \"GITHUB_TOKEN\"}' | jq -r '.value')\ngit clone \"https://${TOKEN}@github.com/org/repo.git\"\n```\n\n### Node.js\n\n```javascript\nconst { execSync } = require(\"child_process\");\n\nfunction readSecret(name) {\n  const raw = execSync(\n    `tai tool secret_read '${JSON.stringify({ name })}'`,\n    { encoding: \"utf-8\" }\n  );\n  return JSON.parse(raw).value;\n}\n\nconst token = readSecret(\"GITHUB_TOKEN\");\n```\n\n### Python\n\n```python\nimport json\nimport subprocess\n\ndef read_secret(name: str) -> str:\n    result = subprocess.run(\n        [\"tai\", \"tool\", \"secret_read\", json.dumps({\"name\": name})],\n        capture_output=True, text=True, check=True,\n    )\n    return json.loads(result.stdout)[\"value\"]\n\ntoken = read_secret(\"GITHUB_TOKEN\")\n```\n\n### PowerShell\n\n```powershell\nfunction Read-Secret {\n    param([string]$Name)\n    $json = @{ name = $Name } | ConvertTo-Json -Compress\n    $result = tai tool secret_read $json | ConvertFrom-Json\n    return $result.value\n}\n\n$token = Read-Secret -Name \"GITHUB_TOKEN\"\n```\n\n## Security Rules\n\n1. **Never print or log secret values** — Do not write secrets to stdout, stderr, or any log file.\n2. **Never write secrets to files** — Exception: SSH keys may be written to `~/.ssh/` with `chmod 600` permissions.\n3. **Never send secrets to the LLM** — Secret values must not appear in prompt content, system messages, or tool call results that are forwarded to the model.\n4. **Scope isolation** — Secrets are scoped per user per agent. An agent can only access secrets configured for it.\n5. **Audit trail** — Every `secret_read` call is logged in the audit trail with the caller's identity.\n","isInternal":false,"tokens":531,"sizeBytes":2106},{"name":"SKILL.md","path":"tools/skills/yao-agent/SKILL.md","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/skills/yao-agent/SKILL.md","title":"yao-agent","category":"anthropic-skill","format":"markdown","content":"---\nname: yao-agent\ndescription: Agent management expert. ALWAYS invoke this skill when you need to list available agents, download or reference agent source code, deploy agent code to the host, or query the LLM connector matrix. Do not guess agent structures — use this skill first.\n---\n\n# Agent Tools\n\nFive tools for managing agents on the host, called via bash.\n\n## agent_list\n\nList available agents. Returns ID, name, description, and capabilities for each agent.\n\n```bash\ntai tool agent_list '{}'\ntai tool agent_list '{\"namespace\": \"smith\"}'\n```\n\n| Parameter   | Type   | Required | Description                                              |\n|-------------|--------|----------|----------------------------------------------------------|\n| `namespace` | string | no       | Filter by namespace (e.g. `yao`, `smith`). Omit for all. |\n\n## agent_download\n\nDownload a **smith-namespace** agent into the development directory for editing. **Restricted to `smith` namespace only** — for other agents, use `agent_reference`.\n\n```bash\ntai tool agent_download '{\"id\": \"smith.weather\"}'\n```\n\n| Parameter | Type   | Required | Description                                              |\n|-----------|--------|----------|----------------------------------------------------------|\n| `id`      | string | yes      | Agent ID in dot notation. Must be `smith.*`.             |\n\nDownloaded code lands in `agent-smith-dev/assistants/smith/<name>/`.\n\n## agent_reference\n\nDownload agent source code from the host into `.references/` for **read-only study**. Any agent across all namespaces can be referenced.\n\n```bash\ntai tool agent_reference '{\"id\": \"yao.slides\"}'\ntai tool agent_reference '{\"id\": \"yao.keeper\"}'\n```\n\n| Parameter | Type   | Required | Description                                        |\n|-----------|--------|----------|----------------------------------------------------|\n| `id`      | string | yes      | Agent ID in dot notation (e.g. `yao.slides`)       |\n\nReferenced code lands in `agent-smith-dev/.references/<namespace>/<name>/`.\n\n## agent_deploy\n\nDeploy agent source code from the sandbox development directory to the host. **Restricted to the `smith` namespace only** — attempts to deploy to other namespaces will be rejected.\n\n```bash\ntai tool agent_deploy '{\"id\": \"smith.weather\"}'\ntai tool agent_deploy '{\"id\": \"smith.weather\", \"message\": \"add SUI page\"}'\n```\n\n| Parameter | Type   | Required | Description                                            |\n|-----------|--------|----------|--------------------------------------------------------|\n| `id`      | string | yes      | Agent ID in dot notation. Must use `smith` namespace.  |\n| `message` | string | no       | Optional deploy message for logging.                   |\n\n## agent_connectors\n\nGet the current user's LLM connector matrix. Returns metadata for each role (default, heavy, light, vision, etc.) **without API keys**. Use this to understand which models are available and their capabilities.\n\n```bash\ntai tool agent_connectors '{}'\n```\n\nNo parameters required.\n\n## Guidelines\n\n- Use `agent_list` to discover agents before downloading or referencing\n- `agent_download` is for editing smith agents — code lands in `agent-smith-dev/assistants/smith/<name>/`\n- `agent_reference` is for studying any agent — code lands in `agent-smith-dev/.references/<namespace>/<name>/`\n- Deploy is restricted to the `smith` namespace for safety\n- Connector data never includes API keys, secrets, or tokens\n- All output is JSON\n","frontmatter":{"name":"yao-agent","description":"Agent management expert. ALWAYS invoke this skill when you need to list available agents, download or reference agent source code, deploy agent code to the host, or query the LLM connector matrix. Do not guess agent structures — use this skill first."},"isInternal":false,"tokens":739,"sizeBytes":3499},{"name":"SKILL.md","path":"tools/skills/yao-audio/SKILL.md","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/skills/yao-audio/SKILL.md","title":"yao-audio","category":"anthropic-skill","format":"markdown","content":"---\nname: yao-audio\ndescription: Audio expert. ALWAYS invoke this skill when the user asks to transcribe, recognize, or convert speech/audio to text.\n---\n\n# Audio Tools\n\nUse these tools to transcribe audio files to text using speech-to-text models.\n\n## audio_transcribe\n\nTranscribe an audio file to text.\n\n```bash\ntai tool audio_transcribe --audio_path /path/to/meeting.m4a\n```\n\n```bash\ntai tool audio_transcribe --audio_path /path/to/recording.wav --language en --provider llm.my-openai:whisper-1\n```\n\n| Parameter  | Type   | Required | Description                                                       |\n| ---------- | ------ | -------- | ----------------------------------------------------------------- |\n| audio_path | string | yes      | Audio file path. Supported: mp3, m4a, wav, webm, mp4, mpeg, mpga  |\n| language   | string | no       | ISO 639-1 language code (e.g. `en`, `zh`, `ja`). Auto-detected if omitted |\n| provider   | string | no       | STT provider connector ID. If omitted, uses the default STT provider |\n\n## audio_providers\n\nList available speech-to-text providers and models.\n\n### List STT providers (default):\n```bash\ntai tool audio_providers\n```\n\n| Parameter  | Type   | Required | Description                          |\n| ---------- | ------ | -------- | ------------------------------------ |\n| capability | string | no       | Filter by capability (default: `audio`) |\n\nReturns a list of providers with their available models and connector IDs that can be passed to `audio_transcribe`.\n\n## Constraints\n\nOnly use the parameters listed above for each tool. Do not pass unsupported parameters — they will be ignored or cause errors.\n","frontmatter":{"name":"yao-audio","description":"Audio expert. ALWAYS invoke this skill when the user asks to transcribe, recognize, or convert speech/audio to text."},"isInternal":false,"tokens":369,"sizeBytes":1663},{"name":"SKILL.md","path":"tools/skills/yao-board/SKILL.md","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/skills/yao-board/SKILL.md","title":"yao-board","category":"anthropic-skill","format":"markdown","content":"---\nname: yao-board\ndescription: Kanban board and task query expert. ALWAYS invoke this skill when the user asks about boards, tasks, task status, or project progress. Do not guess task state — use this skill first.\n---\n\n# Board & Task Tools\n\nTwo tools for querying kanban boards and tasks, called via bash.\n\n## board_list\n\nList all kanban boards with their columns and task counts.\n\n```bash\ntai tool board_list '{}'\n```\n\n## task_list\n\nList tasks with optional filtering.\n\n```bash\ntai tool task_list '{\"board_id\": \"xxx\", \"run_status\": \"running\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `run_status` | string | no | Filter: pending/running/waiting/completed/failed |\n| `assistant_id` | string | no | Filter by assistant ID |\n| `board_id` | string | no | Filter by board ID |\n| `page` | number | no | Page number (default 1) |\n| `page_size` | number | no | Items per page (default 50) |\n\n## Guidelines\n\n- Use board_list first to discover available boards and their IDs\n- Use task_list with filters to find specific tasks\n- All output is JSON\n","frontmatter":{"name":"yao-board","description":"Kanban board and task query expert. ALWAYS invoke this skill when the user asks about boards, tasks, task status, or project progress. Do not guess task state — use this skill first."},"isInternal":false,"tokens":271,"sizeBytes":1102},{"name":"SKILL.md","path":"tools/skills/yao-doc/SKILL.md","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/skills/yao-doc/SKILL.md","title":"yao-doc","category":"anthropic-skill","format":"markdown","content":"---\nname: yao-doc\ndescription: Yao process documentation expert. ALWAYS invoke this skill when the user needs to discover available processes, read process signatures, or validate process names. Do not guess process APIs — use this skill first.\n---\n\n# Documentation Tools\n\nThree tools for browsing Yao process documentation, called via bash.\n\n## doc_list\n\nSearch and list available process documentation entries.\n\n```bash\ntai tool doc_list '{\"keyword\": \"user\", \"limit\": 10}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `keyword` | string | no | Search keyword (empty to list all) |\n| `limit` | integer | no | Max results (default 20) |\n\n## doc_inspect\n\nGet detailed documentation for a specific process: arguments, return type, methods.\n\n```bash\ntai tool doc_inspect '{\"name\": \"models.user.Find\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `name` | string | yes | Process name (e.g. `models.user.Find`) |\n\n## doc_validate\n\nCheck if a process name is valid. Returns suggestions for similar processes if not found.\n\n```bash\ntai tool doc_validate '{\"name\": \"models.user.Findd\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `name` | string | yes | Process name to validate |\n\n## Workflow\n\n1. **doc_list** — discover available processes by keyword\n2. **doc_inspect** — read the full signature before calling\n3. **doc_validate** — fix typos when a process name doesn't work\n\n## Guidelines\n\n- Always use doc_inspect before calling an unfamiliar process via process_call\n- Use doc_validate when you get unexpected errors — the name might be misspelled\n","frontmatter":{"name":"yao-doc","description":"Yao process documentation expert. ALWAYS invoke this skill when the user needs to discover available processes, read process signatures, or validate process names. Do not guess process APIs — use this skill first."},"isInternal":false,"tokens":388,"sizeBytes":1721},{"name":"SKILL.md","path":"tools/skills/yao-image/SKILL.md","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/skills/yao-image/SKILL.md","title":"yao-image","category":"anthropic-skill","format":"markdown","content":"---\nname: yao-image\ndescription: Image expert. ALWAYS invoke this skill when you need to read, analyze, describe, or generate images. Use for screenshots, photos, charts, diagrams, AI-generated images, or any visual content.\n---\n\n# Image Tools\n\nUse these tools when you encounter images you cannot read natively, or when you need to generate new images.\n\n## image_read\n\nSend an image to a vision-capable model and get a text description.\n\n### Local file (most common):\n```bash\ntai tool image_read --image_path /path/to/image.png --prompt \"Describe this image\"\n```\n\n### URL:\n```bash\ntai tool image_read --image_path https://example.com/photo.jpg --prompt \"What is shown?\"\n```\n\n### With a specific vision provider:\n```bash\ntai tool image_read --image_path /path/to/image.png --prompt \"Describe\" --provider llm.my-openai:gpt-4o\n```\n\n| Parameter  | Type    | Required | Description                                                     |\n| ---------- | ------- | -------- | --------------------------------------------------------------- |\n| image_path | string  | yes      | Image file path or URL                                          |\n| prompt     | string  | no       | Analysis instruction (default: describe in detail)              |\n| max_size   | integer | no       | Max dimension in pixels for longest edge (default: 1080)        |\n| provider   | string  | no       | Vision provider connector ID. If omitted, uses default vision model |\n\nImages are automatically resized (preserving aspect ratio) before sending to the vision model.\nSupported formats: PNG, JPEG, GIF, WebP.\n\n## image_generate\n\nGenerate a new image from a text prompt (text-to-image). For editing an existing image, use `image_edit` instead.\n\n### Basic usage (always specify output):\n```bash\ntai tool image_generate --prompt \"A serene mountain landscape at sunset\" --output landscape.png\n```\n\n### With specific provider, model and size:\n```bash\ntai tool image_generate --prompt \"A futuristic city skyline\" --provider llm.my-openai --model gpt-image-1 --dimensions 1792x1024 --output output/city.png\n```\n\n### Transparent background (for icons, stickers, product shots):\n```bash\ntai tool image_generate --prompt \"A cute fox mascot\" --background transparent --output_format png --output fox.png\n```\n\n### WebP output with quality control:\n```bash\ntai tool image_generate --prompt \"A landscape painting\" --output_format webp --quality high --output painting.webp\n```\n\n| Parameter | Type   | Required | Description                                                       |\n| --------- | ------ | -------- | ----------------------------------------------------------------- |\n| prompt     | string | yes      | Text description of the image to generate                         |\n| output     | string | yes      | Output file path for the generated image                          |\n| provider   | string | no       | Provider connector ID (use `image_providers` to list). Auto-selects if omitted |\n| dimensions | string | no       | Image dimensions (default: 1024x1024). Common: 1024x1024, 1024x1792, 1792x1024 |\n| model      | string | no       | Model name to use. Overrides the provider's default model         |\n| background | string | no       | `transparent`, `opaque`, or `auto`. Use `transparent` for PNG/WebP with no background |\n| output_format | string | no    | `png`, `jpeg`, or `webp`. Default: png                            |\n| output_compression | integer | no | Compression level 0-100 for jpeg/webp. Higher = better quality. Default: 100 |\n| quality    | string | no       | `low`, `medium`, `high`, or `auto`. Higher takes longer. Default: auto |\n| extra      | JSON   | no       | Provider-specific parameters as a JSON object (e.g. `--extra '{\"moderation\":\"low\"}'`) |\n\nIf `output` is omitted, the image is saved to a default path in the working directory.\n\n## image_edit\n\nEdit or transform an existing image based on a text prompt (image-to-image). Use for style transfer, background replacement, adding/removing elements, or any modification that requires a reference image.\n\n### Basic usage:\n```bash\ntai tool image_edit --image_path /path/to/photo.png --prompt \"Change the background to a beach scene\" --output edited.png\n```\n\n### With URL image:\n```bash\ntai tool image_edit --image_path https://example.com/photo.jpg --prompt \"Make it look like a watercolor painting\" --output watercolor.png\n```\n\n### With specific provider and model:\n```bash\ntai tool image_edit --image_path /path/to/original.png --prompt \"Remove the person in the foreground\" --provider llm.my-openai --model gpt-image-1 --dimensions 1024x1024 --output result.png\n```\n\n### With mask (edit only the masked region):\n```bash\ntai tool image_edit --image_path /path/to/photo.png --mask /path/to/mask.png --prompt \"Replace with a garden\" --output edited.png\n```\n\n### Transparent background edit:\n```bash\ntai tool image_edit --image_path /path/to/product.png --prompt \"Remove background\" --background transparent --output_format png --output cutout.png\n```\n\n| Parameter  | Type   | Required | Description                                                       |\n| ---------- | ------ | -------- | ----------------------------------------------------------------- |\n| image_path | string | yes      | Reference image file path or URL                                  |\n| prompt     | string | yes      | Text description of the desired edit or transformation            |\n| output     | string | yes      | Output file path for the edited image                             |\n| provider   | string | no       | Provider connector ID (use `image_providers` with `capability=image_editing`). Auto-selects if omitted |\n| dimensions | string | no       | Output dimensions (default: 1024x1024). Common: 1024x1024, 1024x1792, 1792x1024 |\n| model      | string | no       | Model name to use. Overrides the provider's default model         |\n| mask       | string | no       | Mask image path/URL. Transparent areas in the mask define the editable region |\n| background | string | no       | `transparent`, `opaque`, or `auto`. Use `transparent` for PNG/WebP with no background |\n| output_format | string | no    | `png`, `jpeg`, or `webp`. Default: png                            |\n| output_compression | integer | no | Compression level 0-100 for jpeg/webp. Higher = better quality. Default: 100 |\n| quality    | string | no       | `low`, `medium`, `high`, or `auto`. Higher takes longer. Default: auto |\n| extra      | JSON   | no       | Provider-specific parameters as a JSON object (e.g. `--extra '{\"input_fidelity\":\"high\"}'`) |\n\nIf `output` is omitted, the image is saved to a default path in the working directory.\n\n## image_providers\n\nList available image providers filtered by capability.\n\n### List image generation providers (default):\n```bash\ntai tool image_providers\n```\n\n### List image editing providers:\n```bash\ntai tool image_providers --capability image_editing\n```\n\n### List vision (image reading) providers:\n```bash\ntai tool image_providers --capability vision\n```\n\n| Parameter  | Type   | Required | Description                                                 |\n| ---------- | ------ | -------- | ----------------------------------------------------------- |\n| capability | string | no       | `image_generation` (default), `image_editing`, or `vision`  |\n\nReturns a list of providers with their available models and connector IDs that can be passed to `image_generate`, `image_edit`, or `image_read`.\n\n## Constraints\n\nUse only the parameters listed above for each tool. The supported first-class parameters are: `prompt`, `output`, `provider`, `dimensions`, `model`, `background`, `output_format`, `output_compression`, `quality`, `mask` (edit only), and `extra`.\n\nDo **not** pass `n`, `style`, or `response_format` — they are unsupported and will be ignored or cause errors.\n\nFor provider-specific parameters not covered above (e.g. `moderation`, `input_fidelity`), pass them through the `extra` JSON object.\n","frontmatter":{"name":"yao-image","description":"Image expert. ALWAYS invoke this skill when you need to read, analyze, describe, or generate images. Use for screenshots, photos, charts, diagrams, AI-generated images, or any visual content."},"isInternal":false,"tokens":1775,"sizeBytes":7979},{"name":"SKILL.md","path":"tools/skills/yao-process/SKILL.md","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/skills/yao-process/SKILL.md","title":"yao-process","category":"anthropic-skill","format":"markdown","content":"---\nname: yao-process\ndescription: Yao process execution expert. ALWAYS invoke this skill when the user needs to call a Yao process, query data models, run scripts, or check process permissions. Do not call processes without checking this skill first.\n---\n\n# Process Tools\n\nTwo tools for Yao process execution, called via bash.\n\n## process_call\n\nExecute a Yao Process by its fully qualified name.\n\n```bash\ntai tool process_call '{\"name\": \"models.user.Find\", \"args\": [1, {}]}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `name` | string | yes | Process name (e.g. `models.user.Find`) |\n| `args` | array | no | Positional arguments |\n\n## process_allowed\n\nCheck which processes are permitted, or verify a specific process.\n\n```bash\n# List all allowed rules\ntai tool process_allowed '{}'\n\n# Check a specific process\ntai tool process_allowed '{\"name\": \"models.user.Find\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `name` | string | no | Process name to check. Omit to list all rules. |\n\n## Workflow\n\n1. Use **process_allowed** to check what is permitted\n2. Use **doc_inspect** (from yao-doc skill) to understand the process signature\n3. Use **process_call** to execute\n\n## Guidelines\n\n- Always check documentation before calling an unfamiliar process\n- A 403 error means the process is not in the allowed list\n- Process names follow the pattern `group.id.Method` (e.g. `models.user.Find`, `scripts.auth.Check`)\n- Rules use prefix matching: `models.*` matches all model processes\n","frontmatter":{"name":"yao-process","description":"Yao process execution expert. ALWAYS invoke this skill when the user needs to call a Yao process, query data models, run scripts, or check process permissions. Do not call processes without checking this skill first."},"isInternal":false,"tokens":368,"sizeBytes":1587},{"name":"SKILL.md","path":"tools/skills/yao-secret/SKILL.md","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/skills/yao-secret/SKILL.md","title":"yao-secret","category":"anthropic-skill","format":"markdown","content":"---\nname: yao-secret\ndescription: Secret management expert. ALWAYS invoke this skill when you need to read API keys, tokens, or other secrets configured by the user. Never hardcode credentials — use this skill to retrieve them securely.\n---\n\n# Secret Tools\n\nTwo tools for accessing user-configured secrets, called via bash.\n\n## secret_list\n\nList available secret names and descriptions. **Does not return secret values** — use `secret_read` for that.\n\n```bash\ntai tool secret_list '{}'\n```\n\nNo parameters required. Returns secrets configured for the current assistant.\n\n## secret_read\n\nRead a secret value by name. Returns the decrypted value for use in scripts.\n\n```bash\ntai tool secret_read '{\"name\": \"GITHUB_TOKEN\"}'\ntai tool secret_read '{\"name\": \"AWS_SECRET_KEY\"}'\n```\n\n| Parameter | Type   | Required | Description                                              |\n|-----------|--------|----------|----------------------------------------------------------|\n| `name`    | string | yes      | Secret key name (e.g. `GITHUB_TOKEN`, `AWS_SECRET_KEY`)  |\n\n**Security**: Never log, print, or expose the returned secret value in output visible to users.\n\n## Typical Workflow\n\n1. `secret_list` — discover what secrets are available\n2. `secret_read` — retrieve a specific secret by name\n3. Use the value in API calls, git auth, etc.\n\n## Guidelines\n\n- Always call `secret_list` first to check if a required secret exists before reading\n- Never hardcode API keys or tokens — always use `secret_read`\n- Secret values are decrypted at read time; treat them as sensitive\n- If a secret is not found, prompt the user to configure it in their settings\n- All output is JSON\n","frontmatter":{"name":"yao-secret","description":"Secret management expert. ALWAYS invoke this skill when you need to read API keys, tokens, or other secrets configured by the user. Never hardcode credentials — use this skill to retrieve them securely."},"isInternal":false,"tokens":360,"sizeBytes":1670},{"name":"SKILL.md","path":"tools/skills/yao-web/SKILL.md","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/skills/yao-web/SKILL.md","title":"yao-web","category":"anthropic-skill","format":"markdown","content":"---\nname: yao-web\ndescription: Web information retrieval expert. ALWAYS invoke this skill when the user needs to search the web, fetch a URL, or access real-time information beyond training data. Do not guess or use stale knowledge — use this skill first.\n---\n\n# Web Tools\n\nTwo tools for web information retrieval, called via bash.\n\n## web_search\n\nSearch the web for real-time information. Returns structured results with title, URL, and content snippet.\n\n```bash\ntai tool web_search '{\"query\": \"search terms\", \"limit\": 5}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `query` | string | yes | Search query |\n| `limit` | integer | no | Max results (default 10) |\n\n## web_fetch\n\nFetch a web page and return its content in readable format.\n\n```bash\ntai tool web_fetch '{\"url\": \"https://example.com\", \"format\": \"markdown\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `url` | string | yes | Fully-formed URL to fetch |\n| `format` | string | no | `markdown` (default) or `html` |\n\n## Guidelines\n\n- Use web_search to discover URLs, then web_fetch to read the content\n- Include the current year when searching for recent information\n- All output is JSON\n","frontmatter":{"name":"yao-web","description":"Web information retrieval expert. ALWAYS invoke this skill when the user needs to search the web, fetch a URL, or access real-time information beyond training data. Do not guess or use stale knowledge — use this skill first."},"isInternal":false,"tokens":294,"sizeBytes":1259},{"name":"SKILL.md","path":"tools/skills/yao-workspace-config/SKILL.md","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/skills/yao-workspace-config/SKILL.md","title":"yao-workspace-config","category":"anthropic-skill","format":"markdown","content":"---\nname: yao-workspace-config\ndescription: \"Workspace Git identity and credential management. ALWAYS invoke this skill when the user asks about configuring Git user info, adding HTTPS tokens, importing SSH keys, or managing workspace-level Git authentication.\"\n---\n\n# Workspace Git Config Tools\n\nManage workspace-level Git configuration, HTTPS credentials, and SSH keys.\n\n## workspace_git_config\n\nGet or set workspace-level Git configuration values.\n\n```bash\n# Get all config\ntai tool workspace_git_config '{\"workspace_id\":\"ws-xxx\",\"action\":\"get\"}'\n\n# Get specific key\ntai tool workspace_git_config '{\"workspace_id\":\"ws-xxx\",\"action\":\"get\",\"key\":\"user.name\"}'\n\n# Set config\ntai tool workspace_git_config '{\"workspace_id\":\"ws-xxx\",\"action\":\"set\",\"key\":\"user.name\",\"value\":\"John\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| workspace_id | string | yes | Workspace ID |\n| action | string | yes | `get` or `set` |\n| key | string | no | Config key (e.g. `user.name`). Empty returns all. |\n| value | string | for set | Config value |\n\n## workspace_git_credential\n\nManage HTTPS Git credentials (personal access tokens).\n\n```bash\n# Set credential\ntai tool workspace_git_credential '{\"workspace_id\":\"ws-xxx\",\"action\":\"set\",\"host\":\"github.com\",\"token\":\"ghp_xxx\"}'\n\n# List credentials (tokens are not exposed)\ntai tool workspace_git_credential '{\"workspace_id\":\"ws-xxx\",\"action\":\"list\"}'\n\n# Delete credential\ntai tool workspace_git_credential '{\"workspace_id\":\"ws-xxx\",\"action\":\"delete\",\"host\":\"github.com\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| workspace_id | string | yes | Workspace ID |\n| action | string | yes | `set`, `list`, or `delete` |\n| host | string | for set/delete | Git host (e.g. `github.com`) |\n| username | string | no | Username (defaults to `x-access-token`) |\n| token | string | for set | Access token |\n\n## workspace_ssh_key\n\nManage SSH keys for Git authentication.\n\n```bash\n# Import SSH key\ntai tool workspace_ssh_key '{\"workspace_id\":\"ws-xxx\",\"action\":\"import\",\"name\":\"github\",\"private_key\":\"-----BEGIN...\",\"host\":\"github.com\"}'\n\n# List SSH keys (private keys not exposed)\ntai tool workspace_ssh_key '{\"workspace_id\":\"ws-xxx\",\"action\":\"list\"}'\n\n# Delete SSH key\ntai tool workspace_ssh_key '{\"workspace_id\":\"ws-xxx\",\"action\":\"delete\",\"name\":\"github\"}'\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| workspace_id | string | yes | Workspace ID |\n| action | string | yes | `import`, `list`, or `delete` |\n| name | string | for import/delete | Key name |\n| private_key | string | for import | PEM-encoded private key |\n| public_key | string | no | Public key (derived from private_key if omitted) |\n| host | string | no | Git host to associate (e.g. `github.com`) |\n","frontmatter":{"name":"yao-workspace-config","description":"Workspace Git identity and credential management. ALWAYS invoke this skill when the user asks about configuring Git user info, adding HTTPS tokens, importing SSH keys, or managing workspace-level Git authentication."},"isInternal":false,"tokens":684,"sizeBytes":2846},{"name":"SKILL.md","path":"tools/skills/yao-workspace/SKILL.md","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/skills/yao-workspace/SKILL.md","title":"yao-workspace","category":"anthropic-skill","format":"markdown","content":"---\nname: yao-workspace\ndescription: Workspace file I/O expert. ALWAYS invoke this skill when you need to list workspaces, read or write files in a workspace on a remote node, or browse workspace directories. Use this for cross-node file operations — for local sandbox files, use standard filesystem tools instead.\n---\n\n# Workspace Tools\n\nFive tools for managing workspaces and file I/O, called via bash.\n\n## workspace_list\n\nList workspaces with summary info. Use `workspace_get` for full details.\n\n```bash\ntai tool workspace_list '{}'\ntai tool workspace_list '{\"node\": \"tai-node-1\"}'\n```\n\n| Parameter | Type   | Required | Description              |\n|-----------|--------|----------|--------------------------|\n| `node`    | string | no       | Filter by Tai node ID    |\n\n## workspace_get\n\nGet full workspace details: name, owner, node, labels, timestamps.\n\n```bash\ntai tool workspace_get '{\"id\": \"ws-abc123\"}'\n```\n\n| Parameter | Type   | Required | Description   |\n|-----------|--------|----------|---------------|\n| `id`      | string | yes      | Workspace ID  |\n\n## workspace_file_list\n\nList files and directories in a workspace path.\n\n```bash\ntai tool workspace_file_list '{\"id\": \"ws-abc123\"}'\ntai tool workspace_file_list '{\"id\": \"ws-abc123\", \"path\": \"src/components\"}'\n```\n\n| Parameter | Type   | Required | Description                    |\n|-----------|--------|----------|--------------------------------|\n| `id`      | string | yes      | Workspace ID                   |\n| `path`    | string | no       | Directory path (default \".\")   |\n\n## workspace_file_read\n\nRead file content from workspace. Returns text (UTF-8) or base64 encoded.\n\n```bash\ntai tool workspace_file_read '{\"id\": \"ws-abc123\", \"path\": \"README.md\"}'\ntai tool workspace_file_read '{\"id\": \"ws-abc123\", \"path\": \"image.png\", \"encoding\": \"base64\"}'\n```\n\n| Parameter  | Type   | Required | Description                              |\n|------------|--------|----------|------------------------------------------|\n| `id`       | string | yes      | Workspace ID                             |\n| `path`     | string | yes      | File path relative to workspace root     |\n| `encoding` | string | no       | \"text\" (default) or \"base64\"             |\n\n## workspace_file_write\n\nWrite content to a file in workspace. For binary files, use base64 encoding.\n\n```bash\ntai tool workspace_file_write '{\"id\": \"ws-abc123\", \"path\": \"output.txt\", \"content\": \"Hello World\"}'\ntai tool workspace_file_write '{\"id\": \"ws-abc123\", \"path\": \"data.bin\", \"content\": \"base64data...\", \"encoding\": \"base64\"}'\n```\n\n| Parameter  | Type   | Required | Description                              |\n|------------|--------|----------|------------------------------------------|\n| `id`       | string | yes      | Workspace ID                             |\n| `path`     | string | yes      | File path relative to workspace root     |\n| `content`  | string | yes      | File content (text or base64 encoded)    |\n| `encoding` | string | no       | \"text\" (default) or \"base64\"             |\n\n## Typical Workflow\n\n1. `workspace_list` — discover available workspaces\n2. `workspace_get` — get full details (node, labels)\n3. `workspace_file_list` — browse directory structure\n4. `workspace_file_read` — read specific files\n5. `workspace_file_write` — write output files\n\n## Guidelines\n\n- Use `workspace_list` for discovery, `workspace_get` for details (list returns summaries only)\n- For large files (>1MB), prefer local filesystem operations if the workspace is mounted in the sandbox\n- Use `encoding: \"base64\"` for binary files (images, archives, etc.)\n- File paths are relative to the workspace root — do not use absolute paths\n- The current sandbox workspace ID is available via `$CTX_WORKSPACE_ID`\n- All output is JSON\n","frontmatter":{"name":"yao-workspace","description":"Workspace file I/O expert. ALWAYS invoke this skill when you need to list workspaces, read or write files in a workspace on a remote node, or browse workspace directories. Use this for cross-node file operations — for local sandbox files, use standard filesystem tools instead."},"isInternal":false,"tokens":881,"sizeBytes":3770},{"name":"create_schema.json","path":"tools/task/create_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/create_schema.json","title":"Task Skill","category":"anthropic-skill","format":"json","content":"# Task Skill\n\nPath: `tools/task/create_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/create_schema.json)","tokens":300,"sizeBytes":162},{"name":"list_schema.json","path":"tools/task/list_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/list_schema.json","title":"Task Skill","category":"anthropic-skill","format":"json","content":"# Task Skill\n\nPath: `tools/task/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/list_schema.json)","tokens":300,"sizeBytes":158},{"name":"move_schema.json","path":"tools/task/move_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/move_schema.json","title":"Task Skill","category":"anthropic-skill","format":"json","content":"# Task Skill\n\nPath: `tools/task/move_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/move_schema.json)","tokens":300,"sizeBytes":158},{"name":"run_schema.json","path":"tools/task/run_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/run_schema.json","title":"Task Skill","category":"anthropic-skill","format":"json","content":"# Task Skill\n\nPath: `tools/task/run_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/run_schema.json)","tokens":300,"sizeBytes":156},{"name":"stop_schema.json","path":"tools/task/stop_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/stop_schema.json","title":"Task Skill","category":"anthropic-skill","format":"json","content":"# Task Skill\n\nPath: `tools/task/stop_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/task/stop_schema.json)","tokens":300,"sizeBytes":158},{"name":"schema.json","path":"tools/webfetch/schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/webfetch/schema.json","title":"Webfetch Skill","category":"anthropic-skill","format":"json","content":"# Webfetch Skill\n\nPath: `tools/webfetch/schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/webfetch/schema.json)","tokens":300,"sizeBytes":160},{"name":"schema.json","path":"tools/websearch/schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/websearch/schema.json","title":"Websearch Skill","category":"anthropic-skill","format":"json","content":"# Websearch Skill\n\nPath: `tools/websearch/schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/websearch/schema.json)","tokens":300,"sizeBytes":163},{"name":"git_config_schema.json","path":"tools/workspace_config/git_config_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace_config/git_config_schema.json","title":"Workspace_config Skill","category":"anthropic-skill","format":"json","content":"# Workspace_config Skill\n\nPath: `tools/workspace_config/git_config_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace_config/git_config_schema.json)","tokens":300,"sizeBytes":206},{"name":"git_credential_schema.json","path":"tools/workspace_config/git_credential_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace_config/git_credential_schema.json","title":"Workspace_config Skill","category":"anthropic-skill","format":"json","content":"# Workspace_config Skill\n\nPath: `tools/workspace_config/git_credential_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace_config/git_credential_schema.json)","tokens":300,"sizeBytes":214},{"name":"ssh_key_schema.json","path":"tools/workspace_config/ssh_key_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace_config/ssh_key_schema.json","title":"Workspace_config Skill","category":"anthropic-skill","format":"json","content":"# Workspace_config Skill\n\nPath: `tools/workspace_config/ssh_key_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace_config/ssh_key_schema.json)","tokens":300,"sizeBytes":200},{"name":"file_list_schema.json","path":"tools/workspace/file_list_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/file_list_schema.json","title":"Workspace Skill","category":"anthropic-skill","format":"json","content":"# Workspace Skill\n\nPath: `tools/workspace/file_list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/file_list_schema.json)","tokens":300,"sizeBytes":183},{"name":"file_read_schema.json","path":"tools/workspace/file_read_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/file_read_schema.json","title":"Workspace Skill","category":"anthropic-skill","format":"json","content":"# Workspace Skill\n\nPath: `tools/workspace/file_read_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/file_read_schema.json)","tokens":300,"sizeBytes":183},{"name":"file_write_schema.json","path":"tools/workspace/file_write_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/file_write_schema.json","title":"Workspace Skill","category":"anthropic-skill","format":"json","content":"# Workspace Skill\n\nPath: `tools/workspace/file_write_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/file_write_schema.json)","tokens":300,"sizeBytes":185},{"name":"get_schema.json","path":"tools/workspace/get_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/get_schema.json","title":"Workspace Skill","category":"anthropic-skill","format":"json","content":"# Workspace Skill\n\nPath: `tools/workspace/get_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/get_schema.json)","tokens":300,"sizeBytes":171},{"name":"list_schema.json","path":"tools/workspace/list_schema.json","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/list_schema.json","title":"Workspace Skill","category":"anthropic-skill","format":"json","content":"# Workspace Skill\n\nPath: `tools/workspace/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/workspace/list_schema.json)","tokens":300,"sizeBytes":173},{"name":"a2a.md","path":"tools/agents/a2a.md","rawUrl":"https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agents/a2a.md","title":"Subagent: a2a","category":"subagent-persona","format":"markdown","content":"# Subagent: a2a\n\nPath: `tools/agents/a2a.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agents/a2a.md)","tokens":300,"sizeBytes":145}],"systemPromptSnippet":"<yakaai_skills repo=\"YaoApp/yao\">\n<!-- File: grpc/DESIGN.md (Tokens: ~4886 | Category: project-spec) -->\n# Yao gRPC Server\n\nGeneral-purpose gRPC gateway for the Yao process. Shares OAuth + ACL scope system with openapi — one token, two protocols.\n\n## Services\n\n| Layer | Method | Purpose | Scope |\n|-------|--------|---------|-------|\n| **Base** | `Run` | Execute Yao process, return result | `grpc:run` |\n| | `Stream` | Execute Yao process, stream output | `grpc:stream` |\n| | `Shell` | Execute system command, wait for result | `grpc:shell` |\n| | `ShellStream` | Execute system command, stream stdout/stderr | `grpc:shell` |\n| **API** | `API` | Proxy to openapi, any endpoint | openapi's own scopes |\n| **MCP** | `MCPListTools` | List MCP tools for a session | `grpc:mcp` |\n| | `MCPCallTool` | Call MCP tool → process.Exec() | `grpc:mcp` |\n| | `MCPListResources` | List MCP resources | `grpc:mcp` |\n| | `MCPReadResource` | Read MCP resource | `grpc:mcp` |\n| **LLM** | `ChatCompletions` | Send messages to LLM, get response | `grpc:llm` |\n| | `ChatCompletionsStream` | Stream LLM response (SSE → gRPC stream) | `grpc:llm` |\n| **Agent** | `AgentStream` | Call agent, stream response | `grpc:agent` |\n\n## Clients\n\n- Container MCP tools (via Tai gRPC relay)\n- `yao run` CLI (after `yao login`)\n- Yao-to-Yao (cross-node process execution)\n\n## Auth\n\nSame as openapi. gRPC auth interceptor reuses the same `guard.Authenticate` logic — including automatic token refresh when access token is expired but refresh token is valid.\n\n```\nmetadata (Bearer + x-refresh-token)\n    → VerifyToken\n    → expired? → TryRefresh (same as guard.go) → new tokens in response metadata\n    → extract scopes → acl.Scope.Check(method, path, scopes)\n```\n\n### Infrastructure reuse assessment\n\nExisting openapi/oauth infrastructure can be reused for gRPC with **zero modifications**:\n\n| Component | Reusable as-is | Notes |\n|-----------|---------------|-------|\n| `VerifyToken(token string)` | Yes | Pure string input, no Gin dependency |\n| `MakeAccessToken(clientID, scope, subject, expiresIn, extraClaims...)` | Yes | Supports custom scope/subject for container tokens |\n| `MakeRefreshToken(...)` | Yes | Same as above |\n| `Revoke(ctx, token, tokenTypeHint)` | Yes | For container token cleanup on Remove |\n| `ScopeManager.Check(req *AccessRequest)` | Yes | Only needs `(Method, Path, Scopes)` — no Gin dependency |\n| `acl.Register(...)` | Yes | gRPC scopes registered via same pattern |\n\nThe `authorized.SetInfo` / `authorized.GetInfo` are Gin-bound but **not needed** — gRPC interceptor builds `AccessRequest` directly from JWT claims. Full `Enforce` chain (client/team/member) is HTTP multi-tenant only; gRPC uses `VerifyToken → ScopeManager.Check` which is sufficient.\n\nNew code required: ~80 lines (interceptor + scope registration). Existing code changes: **zero**.\n\n### CLI auth: `yao login` / `yao logout`\n\nOAuth 2.0 Device Authorization Grant. No `--remote` flag needed — logged in = gRPC, not logged in = local.\n\n```\n$ yao login --server https://yao.example.com\n请访问: https://yao.example.com/device\n输入代码: ABCD-1234\n等待授权... ✓ (token saved to ~/.yao/credentials)\n\n$ yao run models.user.Find '{\"id\":1}'    ← auto gRPC\n$ yao logout\n```\n\nRequires two new openapi endpoints:\n- `POST /oauth/device/authorize` — issue device_code + user_code\n- `POST /oauth/device/token` — poll for access_token\n\nToken scope: based on user's role, e.g. `grpc:run grpc:stream grpc:shell grpc:llm grpc:agent grpc:mcp`.\n\n**Implementation cost**: ~190 lines new code, ~10 lines changes to existing code.\nScaffolding already in place — `types.DeviceAuthorizationResponse`, `GrantTypeDeviceCode`, error codes (`ErrorAuthorizationPending`, `ErrorSlowDown`), `DeviceCodeLifetime` config, `DeviceAuthorization()` method signature, and HTTP route are all pre-defined. Core work:\n\n1. Implement `DeviceAuthorization()` in `device.go` (currently returns `nil, nil`)\n2. Add device_code store/get/consume helpers in `token.go`\n3. Add `GrantTypeDeviceCode` case to `Token()` switch in `core.go` (1 case branch)\n4. Implement `handleDeviceCodeGrant()` in `core.go`\n5. Add user authorization callback handler\n6. Fix discovery endpoint path inconsistency (`/oauth/device` vs `/oauth/device_authorization`)\n\nRisk: **very low** — all additions are in isolated code paths, no changes to existing `authorization_code` / `client_credentials` / `refresh_token` flows.\n\n### Container token\n\nContainer images and `yao-grpc` (`yao/tai/grpc/`) are ours — it handles token refresh automatically.\n\n```\nManager creates container\n    ├─ oauth.MakeAccessToken(subject=userID, scope=\"grpc:mcp grpc:run\")\n    ├─ oauth.MakeRefreshToken(...)\n    └─ tai.Client.Sandbox().Create(CreateRequest{\n           Env: {\n               YAO_TOKEN, YAO_REFRESH_TOKEN, YAO_SANDBOX_ID,\n               YAO_GRPC_ADDR,                 // where to connect\n               YAO_GRPC_UPSTREAM,             // remote only: where Tai should forward to\n           },\n       })\n\n       Local:  YAO_GRPC_ADDR=127.0.0.1:9099    (direct to Yao, no upstream needed)\n       Remote: YAO_GRPC_ADDR=tai-host:9100      YAO_GRPC_UPSTREAM=yao-host:9099\n\nyao-grpc (tai/grpc/, container-internal)\n    ├─ reads YAO_GRPC_ADDR + YAO_TOKEN + YAO_REFRESH_TOKEN + YAO_SANDBOX_ID from env\n    ├─ if YAO_GRPC_UPSTREAM set: attaches x-grpc-upstream metadata (tells Tai where to forward)\n    ├─ every call: Bearer token + x-refresh-token + x-sandbox-id in gRPC metadata\n    ├─ server auth interceptor reuses guard.Authenticate logic:\n    │   token valid → pass through\n    │   token expired + refresh token present → auto rotate (same as HTTP guard)\n    │   new tokens returned via response metadata (x-access-token, x-refresh-token)\n    ├─ yao-grpc reads response metadata, updates tokens in memory\n    └─ transparent to caller, no separate refresh RPC needed\n```\n\n- access_token: short TTL (15m)\n- refresh_token: no expiry (valid until container removed)\n- Manager revokes refresh_token on container Remove\n- Tai does NOT know Yao address at startup — yao-grpc carries target in request metadata\n\n### Virtual endpoint mapping\n\n| gRPC | Virtual endpoint |\n|------|-----------------|\n| Run(\"models.user.Find\") | `POST /grpc/run/models.user.Find` |\n| Stream(\"flows.report\") | `POST /grpc/stream/flows.report` |\n| Shell | `POST /grpc/shell` |\n| ShellStream | `POST /grpc/shell` (same) |\n| API(POST, /kb/collections) | `POST /kb/collections` (real openapi path) |\n| MCPListTools | `GET /grpc/mcp/tools` |\n| MCPCallTool(\"search\") | `POST /grpc/mcp/call/search` |\n| MCPListResources | `GET /grpc/mcp/resources` |\n| MCPReadResource(\"uri\") | `GET /grpc/mcp/resources/read` |\n| ChatCompletions | `POST /grpc/llm/completions` |\n| ChatCompletionsStream | `POST /grpc/llm/completions` (same) |\n| AgentStream(\"robot-id\") | `POST /grpc/agent/robot-id` |\n\nAPI method uses the **actual openapi path** — no virtual mapping needed, scope check is identical to HTTP.\n\n### Scope registration\n\n```go\nfunc init() {\n    acl.Register(\n        &acl.ScopeDefinition{Name: \"grpc:run\",    Endpoints: []string{\"POST /grpc/run/*\"}},\n        &acl.ScopeDefinition{Name: \"grpc:stream\", Endpoints: []string{\"POST /grpc/stream/*\"}},\n        &acl.ScopeDefinition{Name: \"grpc:shell\",  Endpoints: []string{\"POST /grpc/shell\"}},\n        &acl.ScopeDefinition{Name: \"grpc:mcp\",    Endpoints: []string{\"GET /grpc/mcp/tools\", \"POST /grpc/mcp/call/*\", \"GET /grpc/mcp/resources\", \"GET /grpc/mcp/resources/read\"}},\n        &acl.ScopeDefinition{Name: \"grpc:llm\",    Endpoints: []string{\"POST /grpc/llm/completions\"}},\n        &acl.ScopeDefinition{Name: \"grpc:agent\", Endpoints: []string{\"POST /grpc/agent/*\"}},\n    )\n}\n```\n\n## Network\n\n### Server listen config\n\n| Env | Default | Purpose |\n|-----|---------|---------|\n| `YAO_GRPC_HOST` | `127.0.0.1` | Comma-separated bind addresses. |\n| `YAO_GRPC_PORT` | `9099` | Listen port (shared by all addresses). |\n| `YAO_GRPC` | _(unset)_ | Set `off` to explicitly disable gRPC server. |\n\ngRPC server **defaults to enabled** (`127.0.0.1:9099`) — sandbox container callbacks depend on it.\n\n`YAO_GRPC_HOST` accepts one or more addresses separated by `,`. Each address gets its own `net.Listener`; all listeners feed into the same `grpc.Server` (gRPC supports multiple `Serve` calls on one server).\n\n| Scenario | Config | Effect |\n|----------|--------|--------|\n| Local dev / default | _(nothing to set)_ | `127.0.0.1:9099` — loopback, sandbox works out of box |\n| LAN multi-NIC | `YAO_GRPC_HOST=192.168.10.1,10.0.0.1` | Binds each internal IP |\n| Open | `YAO_GRPC_HOST=0.0.0.0` | All interfaces |\n| Disabled | `YAO_GRPC=off` | gRPC server not started (pure API gateway, no sandbox) |\n\nWhen multiple addresses are given, the server creates one goroutine per listener. Shutdown (`grpc.GracefulStop`) drains all listeners.\n\nConfig lives in `config.Config.GRPC` (type `GRPCConfig`), same pattern as `Host`/`Port` for HTTP.\n\n### Startup\n\ngRPC server starts **after** HTTP server in `cmd/start.go`, as a parallel goroutine:\n\n```\nengine.Load → itask.Start → ischedule.Start → service.Start (HTTP) → grpc.StartServer (gRPC)\n```\n\ngRPC server starts by default. Set `YAO_GRPC=off` to explicitly disable (no-op startup). Any other value or unset means enabled.\n\nShutdown: `defer grpc.Stop()` in `cmd/start.go`, called before HTTP stop for graceful drain.\n\n### Access control\n\nLocal: containers and CLI connect via loopback. Remote: only Tai relay connects (address known from `YAO_TAI_ADDR`). All callers carry OAuth tokens — no IP allowlist needed.\n\nInterceptor chain: auth → ACL → handler.\n\nPublic methods (skip auth): `Healthz`. Auth interceptor checks method name and passes through.\n\n## IPC Path (replacing Unix socket)\n\nAll modes use gRPC — no Unix socket fallback. One code path, local and remote.\n\n```\nLocal:   Container → yao-grpc → Yao gRPC 127.0.0.1:9099\nRemote:  Container → yao-grpc → Tai :9100 relay → Yao gRPC :9099\n```\n\n`yao-grpc` reads `YAO_GRPC_ADDR` from env and connects. Local containers point directly at the Yao gRPC server on loopback; remote containers point at the Tai relay. No mode switch, no branching.\n\n### Tai relay routing\n\nTai does **not** know the Yao gRPC address at startup. yao-grpc tells Tai where to forward on every request via metadata:\n\n```\nManager.Create(sandbox)\n    ├─ oauth.MakeAccessToken(...)\n    ├─ oauth.MakeRefreshToken(...)\n    └─ tai.Client.Sandbox().Create(CreateRequest{\n           Env: {\n               YAO_TOKEN, YAO_REFRESH_TOKEN,\n               YAO_GRPC_ADDR: \"tai-host:9100\",\n               YAO_GRPC_UPSTREAM: \"yao-host:9099\",\n           },\n       })\n```\n\nyao-grpc reads `YAO_GRPC_UPSTREAM` from env and attaches it as `x-grpc-upstream` metadata on every request to Tai. Tai gateway reads this metadata and forwards to the specified address. No per-container state in Tai, no lookup table — pure transparent proxy. One Tai can serve containers from different Yao instances because each request carries its own target.\n\nFor local mode, no Tai relay — Manager injects `YAO_GRPC_ADDR=127.0.0.1:9099` directly (no `YAO_GRPC_UPSTREAM` needed).\n\n### yao-grpc (container client)\n\n`yao-grpc` is the in-container gRPC client binary. Replaces the old `yao-bridge`. Lives in `yao/tai/grpc/`:\n\n```\nyao/tai/grpc/\n├── grpc.go             // gRPC client: connect, forward MCP/process calls\n├── auth.go             // token management: read env, auto-refresh\n├── grpc_test.go\n└── cmd/\n    └── main.go\n```\n\nRationale for placing in `yao/tai`:\n- Consumes Tai relay — same layer as `tai/proxy`, `tai/volume`\n- Shares gRPC deps already in `yao/tai`\n- Version-locked with Tai SDK and server protocol\n- Built in same CI: `go build -o yao-grpc ./tai/grpc/cmd`\n\nPure client — no signing keys, no `oauth` package dependency. Reads `YAO_TOKEN` + `YAO_REFRESH_TOKEN` + `YAO_SANDBOX_ID` from env, attaches all three as gRPC metadata on every call. Token refresh is transparent — server auto-rotates expired tokens (same logic as HTTP guard) and returns new tokens via response metadata.\n\n## Proto\n\n```protobuf\nservice Yao {\n  // Base\n  rpc Run(RunRequest) returns (RunResponse);\n  rpc Stream(RunRequest) returns (stream Chunk);\n  rpc Shell(ShellRequest) returns (ShellResponse);\n  rpc ShellStream(ShellRequest) returns (stream Chunk);\n\n  // API gateway\n  rpc API(APIRequest) returns (APIResponse);\n\n  // MCP\n  rpc MCPListTools(MCPListRequest) returns (MCPListResponse);\n  rpc MCPCallTool(MCPCallRequest) returns (MCPCallResponse);\n  rpc MCPListResources(MCPListRequest) returns (MCPResourcesResponse);\n  rpc MCPReadResource(MCPResourceRequest) returns (MCPResourceResponse);\n\n  // AI - LLM\n  rpc ChatCompletions(ChatRequest) returns (ChatResponse);\n  rpc ChatCompletionsStream(ChatRequest) returns (stream ChatChunk);\n\n  // AI - Agent\n  rpc AgentStream(AgentRequest) returns (stream AgentChunk);\n\n  // Health\n  rpc Healthz(Empty) returns (HealthzResponse);\n}\n```\n\n### LLM layer\n\n`ChatCompletions` and `ChatCompletionsStream` call the existing `llm.ChatCompletions` process (`agent/llm/process.go`). It auto-detects connector type (openai/anthropic/etc.), selects the appropriate provider, and returns OpenAI-compatible format.\n\n```\ngRPC ChatCompletions(connector, messages, opts)\n    → process.Exec(\"llm.ChatCompletions\", connector, messages, opts)\n    → agent/llm.New(conn, opts) → provider.Stream/Post → response\n\ngRPC ChatCompletionsStream(connector, messages, opts)\n    → same path, with streaming callback → gRPC stream chunks\n```\n\nThe caller specifies a connector ID. The `llm.ChatCompletions` process resolves it via `connector.Select()`, creates the LLM instance, and executes. Streaming version passes a callback that forwards chunks to the gRPC stream.\n\n### Agent layer\n\n`AgentStream` wraps `agent/robots/:id/completions` — resolves robot → host assistant → runs agent pipeline → streams output. Only stream method — agent output is inherently streamed; non-stream callers simply consume all chunks. Internally calls `assistant.Stream()` with `ctx.Writer` set to nil (or noop) when the caller doesn't need incremental output.\n\n```\ngRPC AgentStream(agent_id, messages) → resolve robot → assistant.Stream() → stream chunks\n```\n\nThis enables container-internal agents to call other agents without HTTP, and remote `yao` instances to orchestrate agent pipelines cross-node.\n\n`AgentChunk` carries `agent/output/message.Message` — the same DSL used by HTTP SSE streaming. Each chunk is one JSON-serialized `Message`:\n\n```protobuf\nmessage AgentChunk {\n  bytes data = 1;  // JSON-encoded agent/output/message.Message\n  bool  done = 2;\n}\n```\n\nThe `Message` structure uses `Type` + `Props` to express all content types (text, thinking, tool_call, error, action, event, image, audio, video). Streaming control fields (`chunk_id`, `message_id`, `block_id`, `thread_id`) and delta fields (`delta`, `delta_path`, `delta_action`) are preserved as-is over gRPC — the client merges chunks using the same logic as CUI's SSE consumer.\n\n### Shell execution context\n\n`Shell` and `ShellStream` execute commands in the **Yao host process**, not inside a sandbox container. This is by design — the scope `grpc:shell` is a privileged capability, not granted to container tokens by default. Container-internal commands run via `tai.Client.Sandbox().Exec()`, which is a different path (not exposed as a gRPC method).\n\nSee [pb/yao.proto](./pb/yao.proto) for full message definitions.\n\n## Process & Stream (gou foundation)\n\ngRPC `Run` and `Stream` map to two parallel systems in `gou`:\n\n```\ngou/process/   — execute once, return result     → gRPC Run\ngou/stream/    — execute once, push chunks        → gRPC Stream\n```\n\n### gou/process (existing, unchanged)\n\n```go\ntype Handler func(process *Process) interface{}\n\nprocess.Register(\"scripts\", handler)\np := process.New(\"scripts.foo.bar\", args...)\np.Execute()\nresult := p.Value()\n```\n\n### gou/stream (new package, parallel to process)\n\n```go\ntype Handler func(ctx context.Context, process *Process, send func([]byte) error) error\n\nstream.Register(\"scripts\", handler)\ns := stream.New(\"scripts.foo.bar\", args...)\ns.Execute(ctx, func(chunk []byte) error { ... })\n```\n\n`stream.Process` mirrors `process.Process` fields (Name, Group, Method, ID, Args, Global, Sid, Authorized) but `ctx` is a first-class parameter, not buried in a struct field.\n\n`send` returns error when the receiver disconnects — handler should stop.\n\n### Fallback\n\nIf a stream handler is not registered for a name but a process handler exists, `stream.Execute` falls back to: run the process handler once, JSON-marshal the result, call `send` once.\n\n### Registration\n\n```go\n// gou/process — existing\nprocess.Register(\"models\", modelsHandler)\nprocess.Register(\"scripts\", scriptsHandler)\n\n// gou/stream — new, same namespace\nstream.Register(\"scripts\", scriptsStreamHandler)\nstream.Register(\"llm\", llmStreamHandler)\n```\n\nSame naming convention. A process name can have both a process handler and a stream handler.\n\n### gRPC mapping\n\n```go\nfunc (s *yaoServer) Run(ctx context.Context, req *pb.RunRequest) (*pb.RunResponse, error) {\n    p := process.NewWithContext(ctx, req.Process, args...)\n    if err := p.Execute(); err != nil { return nil, err }\n    data, _ := json.Marshal(p.Value())\n    return &pb.RunResponse{Result: data}, nil\n}\n\nfunc (s *yaoServer) Stream(req *pb.RunRequest, grpcStream pb.Yao_StreamServer) error {\n    st := stream.New(req.Process, args...)\n    return st.Execute(grpcStream.Context(), func(chunk []byte) error {\n        return grpcStream.Send(&pb.Chunk{Data: chunk})\n    })\n}\n```\n\n### V8 integration\n\nBoth are exposed as top-level globals in JavaScript, parallel:\n\n```go\n// gou/runtime/v8/isolate.go MakeTemplate\ntemplate.Set(\"Process\", processModule.ExportFunction(iso))        // existing\ntemplate.Set(\"Stream\",  streamModule.ExportFunction(iso))         // new\n```\n\n**JS calling Go stream** (JS is consumer):\n\n```javascript\nStream(\"llm.chat.completions\", function(chunk) {\n    log.Info(chunk)\n    return 1  // 1=continue, 0=stop\n}, { model: \"gpt-4\", messages: [...] })\n```\n\n**JS script as stream handler** (JS is producer):\n\n```javascript\n// scripts/report.js — registered via stream.Register(\"scripts\", ...)\nfunction generate(args, send) {\n    send(\"part 1\")\n    send(\"part 2\")\n}\n```\n\nV8 runtime registers both:\n\n```go\nfunc init() {\n    process.Register(\"scripts\", processScripts)   // existing\n    stream.Register(\"scripts\", processScriptsStream) // new\n}\n```\n\n`processScriptsStream` calls `script.ExecStream(ctx, p, send)` which injects `send` into the V8 global before executing the script method.\n\n### Impact on existing code\n\n| Component | Changes |\n|-----------|---------|\n| `gou/process/` | None |\n| `gou/stream/` | New package (~150 lines) |\n| `gou/runtime/v8/process.go` | +1 line: `stream.Register(...)` |\n| `gou/runtime/v8/script.go` | +`ExecStream` method |\n| `gou/runtime/v8/isolate.go` | +1 line: `template.Set(\"Stream\", ...)` |\n| `gou/runtime/v8/functions/` | +`stream/` module for JS→Go stream consumption |\n\n<!-- File: workspace/DESIGN.md (Tokens: ~300 | Category: project-spec) -->\n# workspace - UI Design System & Style Tokens\n\nPath: `workspace/DESIGN.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/workspace/DESIGN.md)\n\n<!-- File: tools/agent/call_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Agent Skill\n\nPath: `tools/agent/call_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/call_schema.json)\n\n<!-- File: tools/agent/connectors_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Agent Skill\n\nPath: `tools/agent/connectors_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/connectors_schema.json)\n\n<!-- File: tools/agent/deploy_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Agent Skill\n\nPath: `tools/agent/deploy_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/deploy_schema.json)\n\n<!-- File: tools/agent/download_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Agent Skill\n\nPath: `tools/agent/download_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/download_schema.json)\n\n<!-- File: tools/agent/list_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Agent Skill\n\nPath: `tools/agent/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/list_schema.json)\n\n<!-- File: tools/agent/reference_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Agent Skill\n\nPath: `tools/agent/reference_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/agent/reference_schema.json)\n\n<!-- File: tools/audio/providers_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Audio Skill\n\nPath: `tools/audio/providers_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/audio/providers_schema.json)\n\n<!-- File: tools/audio/transcribe_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Audio Skill\n\nPath: `tools/audio/transcribe_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/audio/transcribe_schema.json)\n\n<!-- File: tools/board/create_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Board Skill\n\nPath: `tools/board/create_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/board/create_schema.json)\n\n<!-- File: tools/board/list_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Board Skill\n\nPath: `tools/board/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/board/list_schema.json)\n\n<!-- File: tools/clip/list_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Clip Skill\n\nPath: `tools/clip/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/clip/list_schema.json)\n\n<!-- File: tools/clip/read_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Clip Skill\n\nPath: `tools/clip/read_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/clip/read_schema.json)\n\n<!-- File: tools/clip/write_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Clip Skill\n\nPath: `tools/clip/write_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/clip/write_schema.json)\n\n<!-- File: tools/docs/inspect.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Docs Skill\n\nPath: `tools/docs/inspect.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/docs/inspect.json)\n\n<!-- File: tools/docs/list.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Docs Skill\n\nPath: `tools/docs/list.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/docs/list.json)\n\n<!-- File: tools/docs/validate.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Docs Skill\n\nPath: `tools/docs/validate.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/docs/validate.json)\n\n<!-- File: tools/image/edit_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Image Skill\n\nPath: `tools/image/edit_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/edit_schema.json)\n\n<!-- File: tools/image/generate_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Image Skill\n\nPath: `tools/image/generate_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/generate_schema.json)\n\n<!-- File: tools/image/providers_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Image Skill\n\nPath: `tools/image/providers_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/providers_schema.json)\n\n<!-- File: tools/image/read_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Image Skill\n\nPath: `tools/image/read_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/image/read_schema.json)\n\n<!-- File: tools/inbox/list_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Inbox Skill\n\nPath: `tools/inbox/list_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/inbox/list_schema.json)\n\n<!-- File: tools/inbox/view_schema.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Inbox Skill\n\nPath: `tools/inbox/view_schema.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/inbox/view_schema.json)\n\n<!-- File: tools/mcps/agent.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Mcps Skill\n\nPath: `tools/mcps/agent.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/agent.json)\n\n<!-- File: tools/mcps/audio.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Mcps Skill\n\nPath: `tools/mcps/audio.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/audio.json)\n\n<!-- File: tools/mcps/clip.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Mcps Skill\n\nPath: `tools/mcps/clip.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/clip.json)\n\n<!-- File: tools/mcps/doc.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Mcps Skill\n\nPath: `tools/mcps/doc.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/doc.json)\n\n<!-- File: tools/mcps/image.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Mcps Skill\n\nPath: `tools/mcps/image.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/image.json)\n\n<!-- File: tools/mcps/kanban.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Mcps Skill\n\nPath: `tools/mcps/kanban.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/YaoApp/yao/HEAD/tools/mcps/kanban.json)\n\n<!-- ... +55 more modular skills available via /api/skills/YaoApp/yao?path={path} -->\n</yakaai_skills>"}