From f91e409a0d9ecb1e802b10a35a2c57beda3cf8cc Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Mon, 9 Mar 2026 19:27:48 +0530 Subject: [PATCH] Enhance development scripts and improve tool documentation - Updated `package.json` scripts for better error handling and streamlined execution of Tauri commands. - Added a new `gmail-skill-integration.md` document detailing the Gmail skill integration process, including OAuth2 flow and tool definitions. - Improved formatting and consistency in `TOOLS.md` for better readability and clarity. - Enhanced logging in skill management and transport layers for better debugging and monitoring of tool calls. --- ai/TOOLS.md | 681 ++++++++----------- docs/gmail-skill-integration.md | 630 +++++++++++++++++ package.json | 5 +- src-tauri/.gitignore | 2 + src-tauri/Cargo.lock | 195 +----- src-tauri/Cargo.toml | 11 +- src-tauri/build.rs | 47 +- src-tauri/scripts/download-tdlib.sh | 80 +++ src/components/oauth/OAuthProviderButton.tsx | 14 +- src/components/oauth/providerConfigs.tsx | 28 +- src/lib/skills/manager.ts | 6 +- src/lib/skills/runtime.ts | 5 +- src/lib/skills/transport.ts | 11 +- src/pages/Conversations.tsx | 123 +++- src/services/api/inferenceApi.ts | 36 +- src/services/daemonHealthService.ts | 21 +- 16 files changed, 1234 insertions(+), 661 deletions(-) create mode 100644 docs/gmail-skill-integration.md create mode 100755 src-tauri/scripts/download-tdlib.sh diff --git a/ai/TOOLS.md b/ai/TOOLS.md index 5d2bc7250..06f9c42e3 100644 --- a/ai/TOOLS.md +++ b/ai/TOOLS.md @@ -7,13 +7,13 @@ This document lists all available tools that AlphaHuman can use to interact with AlphaHuman has access to **104 tools** across **3 integrations**. **Quick Statistics:** + - **Github**: 72 tools - **Gmail**: 7 tools - **Notion**: 25 tools ## Available Tools - ### Github Tools This skill provides 72 tools for github integration. @@ -23,6 +23,7 @@ This skill provides 72 tools for github integration. **Description**: List repositories for the authenticated user or a specific owner **Parameters**: + - **limit** (number): Maximum number of repositories to return - **owner** (string): Repository owner (user or org). Defaults to the authenticated user - **sort** (string): Sort field @@ -31,6 +32,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "list-repos", @@ -50,20 +52,16 @@ This skill provides 72 tools for github integration. **Description**: Get detailed information about a specific repository **Parameters**: + - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "get-repo", - "parameters": { - "owner": "example_owner", - "repo": "example_repo" - } -} +{ "tool": "get-repo", "parameters": { "owner": "example_owner", "repo": "example_repo" } } ``` --- @@ -73,6 +71,7 @@ This skill provides 72 tools for github integration. **Description**: Create a new repository for the authenticated user **Parameters**: + - **auto_init** (boolean): Initialize with a README - **description** (string): Repository description - **name** (string) **(required)**: Repository name @@ -81,6 +80,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "create-repo", @@ -100,6 +100,7 @@ This skill provides 72 tools for github integration. **Description**: Fork a repository to the authenticated user's account **Parameters**: + - **fork_name** (string): Custom name for the forked repository - **owner** (string) **(required)**: Owner of the repository to fork - **repo** (string) **(required)**: Repository name to fork @@ -107,6 +108,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "fork-repo", @@ -125,6 +127,7 @@ This skill provides 72 tools for github integration. **Description**: Permanently delete a repository. This action cannot be undone **Parameters**: + - **confirm** (boolean) **(required)**: Must be true to confirm deletion - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name @@ -132,14 +135,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "delete-repo", - "parameters": { - "confirm": true, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "confirm": true, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -150,20 +150,16 @@ This skill provides 72 tools for github integration. **Description**: Get clone URLs for a repository **Parameters**: + - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "clone-repo", - "parameters": { - "owner": "example_owner", - "repo": "example_repo" - } -} +{ "tool": "clone-repo", "parameters": { "owner": "example_owner", "repo": "example_repo" } } ``` --- @@ -173,6 +169,7 @@ This skill provides 72 tools for github integration. **Description**: List collaborators on a repository **Parameters**: + - **limit** (number): Maximum number of collaborators to return - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name @@ -180,14 +177,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "list-collaborators", - "parameters": { - "limit": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "limit": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -198,6 +192,7 @@ This skill provides 72 tools for github integration. **Description**: Add a collaborator to a repository **Parameters**: + - **owner** (string) **(required)**: Repository owner - **permission** (string): Permission level to grant - **repo** (string) **(required)**: Repository name @@ -206,6 +201,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "add-collaborator", @@ -225,6 +221,7 @@ This skill provides 72 tools for github integration. **Description**: Remove a collaborator from a repository **Parameters**: + - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name - **username** (string) **(required)**: GitHub username of the collaborator to remove @@ -232,14 +229,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "remove-collaborator", - "parameters": { - "owner": "example_owner", - "repo": "example_repo", - "username": "example_username" - } + "parameters": { "owner": "example_owner", "repo": "example_repo", "username": "example_username" } } ``` @@ -250,20 +244,16 @@ This skill provides 72 tools for github integration. **Description**: List topics (tags) on a repository **Parameters**: + - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "list-topics", - "parameters": { - "owner": "example_owner", - "repo": "example_repo" - } -} +{ "tool": "list-topics", "parameters": { "owner": "example_owner", "repo": "example_repo" } } ``` --- @@ -273,6 +263,7 @@ This skill provides 72 tools for github integration. **Description**: Replace all topics on a repository **Parameters**: + - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name - **topics** (array) **(required)**: List of topic names to set @@ -280,14 +271,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "set-topics", - "parameters": { - "owner": "example_owner", - "repo": "example_repo", - "topics": [] - } + "parameters": { "owner": "example_owner", "repo": "example_repo", "topics": [] } } ``` @@ -298,20 +286,16 @@ This skill provides 72 tools for github integration. **Description**: List programming languages detected in a repository **Parameters**: + - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "list-languages", - "parameters": { - "owner": "example_owner", - "repo": "example_repo" - } -} +{ "tool": "list-languages", "parameters": { "owner": "example_owner", "repo": "example_repo" } } ``` --- @@ -321,6 +305,7 @@ This skill provides 72 tools for github integration. **Description**: List issues in a repository with optional filters **Parameters**: + - **assignee** (string): Filter by assignee username - **label** (string): Filter by label name - **limit** (number): Maximum number of issues to return @@ -331,6 +316,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "list-issues", @@ -352,6 +338,7 @@ This skill provides 72 tools for github integration. **Description**: Get detailed information about a specific issue **Parameters**: + - **number** (number) **(required)**: Issue number - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name @@ -359,14 +346,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "get-issue", - "parameters": { - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -377,6 +361,7 @@ This skill provides 72 tools for github integration. **Description**: Create a new issue in a repository **Parameters**: + - **assignees** (array): Usernames to assign - **body** (string): Issue body (Markdown supported) - **labels** (array): Labels to apply @@ -387,6 +372,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "create-issue", @@ -408,6 +394,7 @@ This skill provides 72 tools for github integration. **Description**: Close an issue **Parameters**: + - **number** (number) **(required)**: Issue number - **owner** (string) **(required)**: Repository owner - **reason** (string): Reason for closing @@ -416,6 +403,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "close-issue", @@ -435,6 +423,7 @@ This skill provides 72 tools for github integration. **Description**: Reopen a closed issue **Parameters**: + - **number** (number) **(required)**: Issue number - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name @@ -442,14 +431,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "reopen-issue", - "parameters": { - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -460,6 +446,7 @@ This skill provides 72 tools for github integration. **Description**: Edit an existing issue's title or body **Parameters**: + - **body** (string): New issue body - **number** (number) **(required)**: Issue number - **owner** (string) **(required)**: Repository owner @@ -469,6 +456,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "edit-issue", @@ -489,6 +477,7 @@ This skill provides 72 tools for github integration. **Description**: Add a comment to an issue **Parameters**: + - **body** (string) **(required)**: Comment body (Markdown supported) - **number** (number) **(required)**: Issue number - **owner** (string) **(required)**: Repository owner @@ -497,6 +486,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "comment-on-issue", @@ -516,6 +506,7 @@ This skill provides 72 tools for github integration. **Description**: List comments on an issue **Parameters**: + - **limit** (number): Maximum number of comments to return - **number** (number) **(required)**: Issue number - **owner** (string) **(required)**: Repository owner @@ -524,15 +515,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "list-issue-comments", - "parameters": { - "limit": 10, - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "limit": 10, "number": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -543,6 +530,7 @@ This skill provides 72 tools for github integration. **Description**: Add labels to an issue **Parameters**: + - **labels** (array) **(required)**: Labels to add - **number** (number) **(required)**: Issue number - **owner** (string) **(required)**: Repository owner @@ -551,15 +539,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "add-issue-labels", - "parameters": { - "labels": [], - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "labels": [], "number": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -570,6 +554,7 @@ This skill provides 72 tools for github integration. **Description**: Remove labels from an issue **Parameters**: + - **labels** (array) **(required)**: Labels to remove - **number** (number) **(required)**: Issue number - **owner** (string) **(required)**: Repository owner @@ -578,15 +563,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "remove-issue-labels", - "parameters": { - "labels": [], - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "labels": [], "number": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -597,6 +578,7 @@ This skill provides 72 tools for github integration. **Description**: Add assignees to an issue **Parameters**: + - **assignees** (array) **(required)**: Usernames to assign - **number** (number) **(required)**: Issue number - **owner** (string) **(required)**: Repository owner @@ -605,15 +587,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "add-issue-assignees", - "parameters": { - "assignees": [], - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "assignees": [], "number": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -624,6 +602,7 @@ This skill provides 72 tools for github integration. **Description**: Remove assignees from an issue **Parameters**: + - **assignees** (array) **(required)**: Usernames to remove - **number** (number) **(required)**: Issue number - **owner** (string) **(required)**: Repository owner @@ -632,15 +611,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "remove-issue-assignees", - "parameters": { - "assignees": [], - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "assignees": [], "number": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -651,6 +626,7 @@ This skill provides 72 tools for github integration. **Description**: List pull requests in a repository with optional filters **Parameters**: + - **base** (string): Filter by base branch name - **limit** (number): Maximum number of pull requests to return - **owner** (string) **(required)**: Repository owner @@ -660,6 +636,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "list-prs", @@ -680,6 +657,7 @@ This skill provides 72 tools for github integration. **Description**: Get detailed information about a specific pull request **Parameters**: + - **number** (number) **(required)**: Pull request number - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name @@ -687,14 +665,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "get-pr", - "parameters": { - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -705,6 +680,7 @@ This skill provides 72 tools for github integration. **Description**: Create a new pull request **Parameters**: + - **base** (string): The branch to merge into (defaults to repo default branch) - **body** (string): Pull request body (Markdown supported) - **draft** (boolean): Create as a draft pull request @@ -716,6 +692,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "create-pr", @@ -738,6 +715,7 @@ This skill provides 72 tools for github integration. **Description**: Close a pull request without merging **Parameters**: + - **number** (number) **(required)**: Pull request number - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name @@ -745,14 +723,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "close-pr", - "parameters": { - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -763,6 +738,7 @@ This skill provides 72 tools for github integration. **Description**: Reopen a closed pull request **Parameters**: + - **number** (number) **(required)**: Pull request number - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name @@ -770,14 +746,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "reopen-pr", - "parameters": { - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -788,6 +761,7 @@ This skill provides 72 tools for github integration. **Description**: Merge a pull request **Parameters**: + - **commit_message** (string): Custom merge commit message - **delete_branch** (boolean): Delete the head branch after merging - **method** (string): Merge method to use @@ -798,6 +772,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "merge-pr", @@ -819,6 +794,7 @@ This skill provides 72 tools for github integration. **Description**: Edit a pull request's title, body, or base branch **Parameters**: + - **base** (string): New base branch - **body** (string): New pull request body - **number** (number) **(required)**: Pull request number @@ -829,6 +805,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "edit-pr", @@ -850,6 +827,7 @@ This skill provides 72 tools for github integration. **Description**: Add a comment to a pull request **Parameters**: + - **body** (string) **(required)**: Comment body (Markdown supported) - **number** (number) **(required)**: Pull request number - **owner** (string) **(required)**: Repository owner @@ -858,6 +836,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "comment-on-pr", @@ -877,6 +856,7 @@ This skill provides 72 tools for github integration. **Description**: List comments on a pull request **Parameters**: + - **limit** (number): Maximum number of comments to return - **number** (number) **(required)**: Pull request number - **owner** (string) **(required)**: Repository owner @@ -885,15 +865,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "list-pr-comments", - "parameters": { - "limit": 10, - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "limit": 10, "number": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -904,6 +880,7 @@ This skill provides 72 tools for github integration. **Description**: List reviews on a pull request **Parameters**: + - **number** (number) **(required)**: Pull request number - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name @@ -911,14 +888,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "list-pr-reviews", - "parameters": { - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -929,6 +903,7 @@ This skill provides 72 tools for github integration. **Description**: Submit a review on a pull request **Parameters**: + - **body** (string): Review comment body - **event** (string) **(required)**: Review action to perform - **number** (number) **(required)**: Pull request number @@ -938,6 +913,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "create-pr-review", @@ -958,6 +934,7 @@ This skill provides 72 tools for github integration. **Description**: List files changed in a pull request **Parameters**: + - **number** (number) **(required)**: Pull request number - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name @@ -965,14 +942,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "list-pr-files", - "parameters": { - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -983,6 +957,7 @@ This skill provides 72 tools for github integration. **Description**: Get the unified diff for a pull request **Parameters**: + - **number** (number) **(required)**: Pull request number - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name @@ -990,14 +965,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "get-pr-diff", - "parameters": { - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -1008,6 +980,7 @@ This skill provides 72 tools for github integration. **Description**: Get CI/CD check runs and status for a pull request **Parameters**: + - **number** (number) **(required)**: Pull request number - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name @@ -1015,14 +988,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "get-pr-checks", - "parameters": { - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -1033,6 +1003,7 @@ This skill provides 72 tools for github integration. **Description**: Request reviews from specific users on a pull request **Parameters**: + - **number** (number) **(required)**: Pull request number - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name @@ -1041,15 +1012,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "request-pr-reviewers", - "parameters": { - "number": 10, - "owner": "example_owner", - "repo": "example_repo", - "reviewers": [] - } + "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo", "reviewers": [] } } ``` @@ -1060,6 +1027,7 @@ This skill provides 72 tools for github integration. **Description**: Mark a draft pull request as ready for review **Parameters**: + - **number** (number) **(required)**: Pull request number - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name @@ -1067,14 +1035,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "mark-pr-ready", - "parameters": { - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -1085,6 +1050,7 @@ This skill provides 72 tools for github integration. **Description**: Search GitHub repositories by query **Parameters**: + - **limit** (number): Maximum number of results to return - **order** (string): Sort order - **query** (string) **(required)**: Search query (supports GitHub search syntax) @@ -1093,6 +1059,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "search-repos", @@ -1112,6 +1079,7 @@ This skill provides 72 tools for github integration. **Description**: Search issues and pull requests across GitHub **Parameters**: + - **limit** (number): Maximum number of results to return - **query** (string) **(required)**: Search query (supports GitHub search syntax, e.g. 'is:issue is:open label:bug') - **sort** (string): Sort field @@ -1119,14 +1087,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "search-issues", - "parameters": { - "limit": 10, - "query": "example_query", - "sort": "example_sort" - } + "parameters": { "limit": 10, "query": "example_query", "sort": "example_sort" } } ``` @@ -1137,6 +1102,7 @@ This skill provides 72 tools for github integration. **Description**: Search code across GitHub repositories **Parameters**: + - **language** (string): Filter by programming language - **limit** (number): Maximum number of results to return - **query** (string) **(required)**: Search query (supports GitHub code search syntax) @@ -1145,6 +1111,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "search-code", @@ -1164,6 +1131,7 @@ This skill provides 72 tools for github integration. **Description**: Search commits across GitHub repositories **Parameters**: + - **limit** (number): Maximum number of results to return - **query** (string) **(required)**: Search query (supports GitHub commit search syntax) - **repo** (string): Restrict search to a specific repo (owner/name format) @@ -1171,14 +1139,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "search-commits", - "parameters": { - "limit": 10, - "query": "example_query", - "repo": "example_repo" - } + "parameters": { "limit": 10, "query": "example_query", "repo": "example_repo" } } ``` @@ -1189,6 +1154,7 @@ This skill provides 72 tools for github integration. **Description**: View the contents of a file in a repository **Parameters**: + - **owner** (string) **(required)**: Repository owner - **path** (string) **(required)**: File path within the repository - **ref** (string): Git ref (branch, tag, or commit SHA). Defaults to the default branch @@ -1197,6 +1163,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "view-file", @@ -1216,6 +1183,7 @@ This skill provides 72 tools for github integration. **Description**: List the contents of a directory in a repository **Parameters**: + - **owner** (string) **(required)**: Repository owner - **path** (string): Directory path within the repository. Defaults to the root - **ref** (string): Git ref (branch, tag, or commit SHA). Defaults to the default branch @@ -1224,6 +1192,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "list-directory", @@ -1243,20 +1212,16 @@ This skill provides 72 tools for github integration. **Description**: Get the README file for a repository **Parameters**: + - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "get-readme", - "parameters": { - "owner": "example_owner", - "repo": "example_repo" - } -} +{ "tool": "get-readme", "parameters": { "owner": "example_owner", "repo": "example_repo" } } ``` --- @@ -1266,6 +1231,7 @@ This skill provides 72 tools for github integration. **Description**: List releases for a repository **Parameters**: + - **limit** (number): Maximum number of releases to return - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name @@ -1273,14 +1239,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "list-releases", - "parameters": { - "limit": 10, - "owner": "example_owner", - "repo": "example_repo" - } + "parameters": { "limit": 10, "owner": "example_owner", "repo": "example_repo" } } ``` @@ -1291,6 +1254,7 @@ This skill provides 72 tools for github integration. **Description**: Get a specific release by tag name **Parameters**: + - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name - **tag** (string) **(required)**: Release tag name (e.g. 'v1.0.0') @@ -1298,14 +1262,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "get-release", - "parameters": { - "owner": "example_owner", - "repo": "example_repo", - "tag": "example_tag" - } + "parameters": { "owner": "example_owner", "repo": "example_repo", "tag": "example_tag" } } ``` @@ -1316,6 +1277,7 @@ This skill provides 72 tools for github integration. **Description**: Create a new release for a repository **Parameters**: + - **draft** (boolean): Create as a draft release - **generate_notes** (boolean): Auto-generate release notes from commits - **notes** (string): Release notes body (Markdown supported) @@ -1329,6 +1291,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "create-release", @@ -1353,6 +1316,7 @@ This skill provides 72 tools for github integration. **Description**: Delete a release by tag name **Parameters**: + - **cleanup_tag** (boolean): Also delete the associated git tag - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name @@ -1361,6 +1325,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "delete-release", @@ -1380,6 +1345,7 @@ This skill provides 72 tools for github integration. **Description**: List assets (downloadable files) attached to a release **Parameters**: + - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name - **tag** (string) **(required)**: Release tag name @@ -1387,14 +1353,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "list-release-assets", - "parameters": { - "owner": "example_owner", - "repo": "example_repo", - "tag": "example_tag" - } + "parameters": { "owner": "example_owner", "repo": "example_repo", "tag": "example_tag" } } ``` @@ -1405,20 +1368,16 @@ This skill provides 72 tools for github integration. **Description**: Get the latest published release for a repository **Parameters**: + - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "get-latest-release", - "parameters": { - "owner": "example_owner", - "repo": "example_repo" - } -} +{ "tool": "get-latest-release", "parameters": { "owner": "example_owner", "repo": "example_repo" } } ``` --- @@ -1428,20 +1387,16 @@ This skill provides 72 tools for github integration. **Description**: List gists for the authenticated user or a specific user **Parameters**: + - **limit** (number): Maximum number of gists to return - **username** (string): GitHub username. Defaults to the authenticated user **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "list-gists", - "parameters": { - "limit": 10, - "username": "example_username" - } -} +{ "tool": "list-gists", "parameters": { "limit": 10, "username": "example_username" } } ``` --- @@ -1451,18 +1406,15 @@ This skill provides 72 tools for github integration. **Description**: Get a specific gist by ID, including its files and content **Parameters**: + - **gist_id** (string) **(required)**: The gist ID **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "get-gist", - "parameters": { - "gist_id": "example_gist_id" - } -} +{ "tool": "get-gist", "parameters": { "gist_id": "example_gist_id" } } ``` --- @@ -1472,6 +1424,7 @@ This skill provides 72 tools for github integration. **Description**: Create a new gist with one or more files **Parameters**: + - **description** (string): Gist description - **files** (object) **(required)**: Map of filename to file content, e.g. {"hello.py": {"content": "print('hello')"}} - **public** (boolean): Whether the gist is public @@ -1479,14 +1432,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "create-gist", - "parameters": { - "description": "example_description", - "files": {}, - "public": true - } + "parameters": { "description": "example_description", "files": {}, "public": true } } ``` @@ -1497,6 +1447,7 @@ This skill provides 72 tools for github integration. **Description**: Edit an existing gist's description or files **Parameters**: + - **description** (string): New gist description - **files** (object): Map of filename to new content. Set content to null to delete a file - **gist_id** (string) **(required)**: The gist ID @@ -1504,14 +1455,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "edit-gist", - "parameters": { - "description": "example_description", - "files": {}, - "gist_id": "example_gist_id" - } + "parameters": { "description": "example_description", "files": {}, "gist_id": "example_gist_id" } } ``` @@ -1522,18 +1470,15 @@ This skill provides 72 tools for github integration. **Description**: Permanently delete a gist **Parameters**: + - **gist_id** (string) **(required)**: The gist ID to delete **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "delete-gist", - "parameters": { - "gist_id": "example_gist_id" - } -} +{ "tool": "delete-gist", "parameters": { "gist_id": "example_gist_id" } } ``` --- @@ -1543,18 +1488,15 @@ This skill provides 72 tools for github integration. **Description**: Get the clone URL for a gist **Parameters**: + - **gist_id** (string) **(required)**: The gist ID to clone **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "clone-gist", - "parameters": { - "gist_id": "example_gist_id" - } -} +{ "tool": "clone-gist", "parameters": { "gist_id": "example_gist_id" } } ``` --- @@ -1564,20 +1506,16 @@ This skill provides 72 tools for github integration. **Description**: List GitHub Actions workflows defined in a repository **Parameters**: + - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "list-workflows", - "parameters": { - "owner": "example_owner", - "repo": "example_repo" - } -} +{ "tool": "list-workflows", "parameters": { "owner": "example_owner", "repo": "example_repo" } } ``` --- @@ -1587,6 +1525,7 @@ This skill provides 72 tools for github integration. **Description**: List recent workflow runs for a repository **Parameters**: + - **branch** (string): Filter by branch name - **limit** (number): Maximum number of runs to return - **owner** (string) **(required)**: Repository owner @@ -1597,6 +1536,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "list-workflow-runs", @@ -1618,6 +1558,7 @@ This skill provides 72 tools for github integration. **Description**: Get detailed information about a specific workflow run **Parameters**: + - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name - **run_id** (number) **(required)**: Workflow run ID @@ -1625,14 +1566,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "get-workflow-run", - "parameters": { - "owner": "example_owner", - "repo": "example_repo", - "run_id": 10 - } + "parameters": { "owner": "example_owner", "repo": "example_repo", "run_id": 10 } } ``` @@ -1643,6 +1581,7 @@ This skill provides 72 tools for github integration. **Description**: List jobs for a specific workflow run **Parameters**: + - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name - **run_id** (number) **(required)**: Workflow run ID @@ -1650,14 +1589,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "list-run-jobs", - "parameters": { - "owner": "example_owner", - "repo": "example_repo", - "run_id": 10 - } + "parameters": { "owner": "example_owner", "repo": "example_repo", "run_id": 10 } } ``` @@ -1668,6 +1604,7 @@ This skill provides 72 tools for github integration. **Description**: Get the logs URL for a specific workflow run **Parameters**: + - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name - **run_id** (number) **(required)**: Workflow run ID @@ -1675,14 +1612,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "get-run-logs", - "parameters": { - "owner": "example_owner", - "repo": "example_repo", - "run_id": 10 - } + "parameters": { "owner": "example_owner", "repo": "example_repo", "run_id": 10 } } ``` @@ -1693,6 +1627,7 @@ This skill provides 72 tools for github integration. **Description**: Re-run an entire workflow run **Parameters**: + - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name - **run_id** (number) **(required)**: Workflow run ID to re-run @@ -1700,14 +1635,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "rerun-workflow", - "parameters": { - "owner": "example_owner", - "repo": "example_repo", - "run_id": 10 - } + "parameters": { "owner": "example_owner", "repo": "example_repo", "run_id": 10 } } ``` @@ -1718,6 +1650,7 @@ This skill provides 72 tools for github integration. **Description**: Cancel a workflow run that is in progress **Parameters**: + - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name - **run_id** (number) **(required)**: Workflow run ID to cancel @@ -1725,14 +1658,11 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "cancel-workflow-run", - "parameters": { - "owner": "example_owner", - "repo": "example_repo", - "run_id": 10 - } + "parameters": { "owner": "example_owner", "repo": "example_repo", "run_id": 10 } } ``` @@ -1743,6 +1673,7 @@ This skill provides 72 tools for github integration. **Description**: Manually trigger a workflow dispatch event **Parameters**: + - **inputs** (object): Input key-value pairs for the workflow_dispatch event - **owner** (string) **(required)**: Repository owner - **ref** (string): Git ref (branch or tag) to run the workflow on @@ -1752,6 +1683,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "trigger-workflow", @@ -1772,6 +1704,7 @@ This skill provides 72 tools for github integration. **Description**: View the YAML source of a workflow definition **Parameters**: + - **owner** (string) **(required)**: Repository owner - **repo** (string) **(required)**: Repository name - **workflow_id** (string) **(required)**: Workflow ID or filename (e.g. 'ci.yml') @@ -1779,6 +1712,7 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "view-workflow-yaml", @@ -1797,20 +1731,16 @@ This skill provides 72 tools for github integration. **Description**: List GitHub notifications for the authenticated user **Parameters**: + - **all** (boolean): Include read notifications - **limit** (number): Maximum number of notifications to return **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "list-notifications", - "parameters": { - "all": true, - "limit": 10 - } -} +{ "tool": "list-notifications", "parameters": { "all": true, "limit": 10 } } ``` --- @@ -1820,18 +1750,15 @@ This skill provides 72 tools for github integration. **Description**: Mark a specific notification thread as read **Parameters**: + - **thread_id** (string) **(required)**: Notification thread ID **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "mark-notification-read", - "parameters": { - "thread_id": "example_thread_id" - } -} +{ "tool": "mark-notification-read", "parameters": { "thread_id": "example_thread_id" } } ``` --- @@ -1840,16 +1767,14 @@ This skill provides 72 tools for github integration. **Description**: Mark all notifications as read -**Parameters**: *None* +**Parameters**: _None_ **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "mark-all-notifications-read", - "parameters": {} -} +{ "tool": "mark-all-notifications-read", "parameters": {} } ``` --- @@ -1859,6 +1784,7 @@ This skill provides 72 tools for github integration. **Description**: Make a raw GitHub REST API request. Use this for any endpoint not covered by the other tools **Parameters**: + - **body** (object): Request body (for POST/PUT/PATCH) - **endpoint** (string) **(required)**: API endpoint path (e.g. '/repos/owner/repo/branches') - **method** (string): HTTP method @@ -1866,20 +1792,16 @@ This skill provides 72 tools for github integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "gh-api", - "parameters": { - "body": {}, - "endpoint": "example_endpoint", - "method": "example_method" - } + "parameters": { "body": {}, "endpoint": "example_endpoint", "method": "example_method" } } ``` --- - ### Gmail Tools This skill provides 7 tools for gmail integration. @@ -1889,6 +1811,7 @@ This skill provides 7 tools for gmail integration. **Description**: Get full details of a specific email by its ID, including headers, body content, and attachments. **Parameters**: + - **format** (string): Message format level (default: full) - **include_body** (boolean): Include email body content (default: true) - **message_id** (string) **(required)**: The Gmail message ID to retrieve @@ -1896,6 +1819,7 @@ This skill provides 7 tools for gmail integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "get-email", @@ -1914,6 +1838,7 @@ This skill provides 7 tools for gmail integration. **Description**: Get emails from Gmail with optional filtering by query, labels, and pagination. Supports Gmail search syntax. When accessToken is provided (e.g. from frontend after OAuth), uses it directly; otherwise uses the skill OAuth credential. **Parameters**: + - **accessToken** (string): Optional OAuth access token. When provided (e.g. by frontend after OAuth), Gmail API is called directly with this token instead of the skill credential. - **include_spam_trash** (boolean): Include emails from spam and trash (default: false) - **label_ids** (array): Filter by specific label IDs (e.g., ["INBOX", "IMPORTANT"]) @@ -1925,6 +1850,7 @@ This skill provides 7 tools for gmail integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "get-emails", @@ -1947,20 +1873,16 @@ This skill provides 7 tools for gmail integration. **Description**: Get all Gmail labels including system and user-created labels with message counts and details. **Parameters**: + - **include_hidden** (boolean): Include hidden labels (default: false) - **type** (string): Filter labels by type (default: all) **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "get-labels", - "parameters": { - "include_hidden": true, - "type": "example_type" - } -} +{ "tool": "get-labels", "parameters": { "include_hidden": true, "type": "example_type" } } ``` --- @@ -1970,18 +1892,15 @@ This skill provides 7 tools for gmail integration. **Description**: Get Gmail user profile information including email address, total message counts, and account details. Optional accessToken for frontend calls. **Parameters**: + - **accessToken** (string): Optional OAuth access token (e.g. from frontend). **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "get-profile", - "parameters": { - "accessToken": "example_accessToken" - } -} +{ "tool": "get-profile", "parameters": { "accessToken": "example_accessToken" } } ``` --- @@ -1991,6 +1910,7 @@ This skill provides 7 tools for gmail integration. **Description**: Mark emails with specific status (read/unread, important, starred) or add/remove labels. **Parameters**: + - **action** (string) **(required)**: Action to perform on the messages - **label_ids** (array): Label IDs to add or remove (required for add_labels/remove_labels actions) - **message_ids** (array) **(required)**: Array of message IDs to modify @@ -1998,14 +1918,11 @@ This skill provides 7 tools for gmail integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "mark-email", - "parameters": { - "action": "example_action", - "label_ids": [], - "message_ids": [] - } + "parameters": { "action": "example_action", "label_ids": [], "message_ids": [] } } ``` @@ -2016,6 +1933,7 @@ This skill provides 7 tools for gmail integration. **Description**: Search emails using advanced Gmail query syntax. Supports complex queries with operators like from:, to:, subject:, has:attachment, is:unread, etc. **Parameters**: + - **include_spam_trash** (boolean): Include results from spam and trash folders (default: false) - **max_results** (number): Maximum number of results to return (default: 20, max: 100) - **page_token** (string): Token for pagination (from previous search) @@ -2024,6 +1942,7 @@ This skill provides 7 tools for gmail integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "search-emails", @@ -2043,6 +1962,7 @@ This skill provides 7 tools for gmail integration. **Description**: Send an email through Gmail with support for HTML/text content, attachments, CC/BCC recipients, and reply threading. **Parameters**: + - **attachments** (array): File attachments (optional) - **bcc** (array): BCC recipients (optional) - **body_html** (string): HTML email body (optional if body_text provided) @@ -2056,6 +1976,7 @@ This skill provides 7 tools for gmail integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "send-email", @@ -2075,7 +1996,6 @@ This skill provides 7 tools for gmail integration. --- - ### Notion Tools This skill provides 25 tools for notion integration. @@ -2085,19 +2005,18 @@ This skill provides 25 tools for notion integration. **Description**: Append child blocks to a page or block. Supports various block types. **Parameters**: + - **block_id** (string) **(required)**: The parent page or block ID - **blocks** (string) **(required)**: JSON string of blocks array. Example: [{"type":"paragraph","paragraph":{"rich_text":[{"text":{"content":"Hello"}}]}}] **Usage Context**: Available in all environments **Example**: + ```json { "tool": "append-blocks", - "parameters": { - "block_id": "example_block_id", - "blocks": "example_blocks" - } + "parameters": { "block_id": "example_block_id", "blocks": "example_blocks" } } ``` @@ -2108,20 +2027,16 @@ This skill provides 25 tools for notion integration. **Description**: Append text content to a page or block. Creates paragraph blocks with the given text. **Parameters**: + - **block_id** (string) **(required)**: The page or block ID to append to - **text** (string) **(required)**: Text content to append **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "append-text", - "parameters": { - "block_id": "example_block_id", - "text": "example_text" - } -} +{ "tool": "append-text", "parameters": { "block_id": "example_block_id", "text": "example_text" } } ``` --- @@ -2131,6 +2046,7 @@ This skill provides 25 tools for notion integration. **Description**: Create a comment on a page or in a discussion thread. Must specify either page_id (for new discussion) or discussion_id (to reply). **Parameters**: + - **discussion_id** (string): Discussion ID to reply to an existing thread - **page_id** (string): Page ID to start a new discussion on - **text** (string) **(required)**: Comment text content @@ -2138,6 +2054,7 @@ This skill provides 25 tools for notion integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "create-comment", @@ -2156,6 +2073,7 @@ This skill provides 25 tools for notion integration. **Description**: Create a new database in Notion. Must specify parent page and property schema. **Parameters**: + - **parent_page_id** (string) **(required)**: Parent page ID where the database will be created - **properties** (string): JSON string of properties schema. Example: {"Name":{"title":{}},"Status":{"select":{"options":[{"name":"Todo"},{"name":"Done"}]}}} - **title** (string) **(required)**: Database title @@ -2163,6 +2081,7 @@ This skill provides 25 tools for notion integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "create-database", @@ -2181,6 +2100,7 @@ This skill provides 25 tools for notion integration. **Description**: Create a new page in Notion. Parent can be another page or a database. For database parents, properties must match the database schema. **Parameters**: + - **content** (string): Initial text content (creates a paragraph block) - **parent_id** (string) **(required)**: Parent page ID or database ID - **parent_type** (string): Type of parent (default: page_id) @@ -2190,6 +2110,7 @@ This skill provides 25 tools for notion integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "create-page", @@ -2210,18 +2131,15 @@ This skill provides 25 tools for notion integration. **Description**: Delete a block. This permanently removes the block from Notion. **Parameters**: + - **block_id** (string) **(required)**: The block ID to delete **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "delete-block", - "parameters": { - "block_id": "example_block_id" - } -} +{ "tool": "delete-block", "parameters": { "block_id": "example_block_id" } } ``` --- @@ -2231,18 +2149,15 @@ This skill provides 25 tools for notion integration. **Description**: Delete (archive) a page. Archived pages can be restored from Notion's trash. **Parameters**: + - **page_id** (string) **(required)**: The page ID to delete/archive **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "delete-page", - "parameters": { - "page_id": "example_page_id" - } -} +{ "tool": "delete-page", "parameters": { "page_id": "example_page_id" } } ``` --- @@ -2252,18 +2167,15 @@ This skill provides 25 tools for notion integration. **Description**: Get a block by its ID. Returns the block's type and content. **Parameters**: + - **block_id** (string) **(required)**: The block ID **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "get-block", - "parameters": { - "block_id": "example_block_id" - } -} +{ "tool": "get-block", "parameters": { "block_id": "example_block_id" } } ``` --- @@ -2273,20 +2185,16 @@ This skill provides 25 tools for notion integration. **Description**: Get the children blocks of a block or page. **Parameters**: + - **block_id** (string) **(required)**: The parent block or page ID - **page_size** (number): Number of blocks (default 50, max 100) **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "get-block-children", - "parameters": { - "block_id": "example_block_id", - "page_size": 10 - } -} +{ "tool": "get-block-children", "parameters": { "block_id": "example_block_id", "page_size": 10 } } ``` --- @@ -2296,18 +2204,15 @@ This skill provides 25 tools for notion integration. **Description**: Get a database's schema and metadata. Shows all properties and their types. **Parameters**: + - **database_id** (string) **(required)**: The database ID **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "get-database", - "parameters": { - "database_id": "example_database_id" - } -} +{ "tool": "get-database", "parameters": { "database_id": "example_database_id" } } ``` --- @@ -2317,18 +2222,15 @@ This skill provides 25 tools for notion integration. **Description**: Get a page's metadata and properties by its ID. Use notion-get-page-content to get the actual content/blocks. **Parameters**: + - **page_id** (string) **(required)**: The page ID (UUID format, with or without dashes) **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "get-page", - "parameters": { - "page_id": "example_page_id" - } -} +{ "tool": "get-page", "parameters": { "page_id": "example_page_id" } } ``` --- @@ -2338,6 +2240,7 @@ This skill provides 25 tools for notion integration. **Description**: Get the content blocks of a page. Returns the text and structure of the page. Use recursive=true to also get nested blocks. **Parameters**: + - **page_id** (string) **(required)**: The page ID to get content from - **page_size** (number): Number of blocks to return (default 50, max 100) - **recursive** (string): Whether to fetch nested blocks (default: false) @@ -2345,14 +2248,11 @@ This skill provides 25 tools for notion integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "get-page-content", - "parameters": { - "page_id": "example_page_id", - "page_size": 10, - "recursive": "example_recursive" - } + "parameters": { "page_id": "example_page_id", "page_size": 10, "recursive": "example_recursive" } } ``` @@ -2363,18 +2263,15 @@ This skill provides 25 tools for notion integration. **Description**: Get a user by their ID. **Parameters**: + - **user_id** (string) **(required)**: The user ID **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "get-user", - "parameters": { - "user_id": "example_user_id" - } -} +{ "tool": "get-user", "parameters": { "user_id": "example_user_id" } } ``` --- @@ -2384,18 +2281,15 @@ This skill provides 25 tools for notion integration. **Description**: List all databases in the workspace that the integration has access to. **Parameters**: + - **page_size** (number): Number of results (default 20, max 100) **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "list-all-databases", - "parameters": { - "page_size": 10 - } -} +{ "tool": "list-all-databases", "parameters": { "page_size": 10 } } ``` --- @@ -2405,18 +2299,15 @@ This skill provides 25 tools for notion integration. **Description**: List all pages in the workspace that the integration has access to. **Parameters**: + - **page_size** (number): Number of results to return (default 20, max 100) **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "list-all-pages", - "parameters": { - "page_size": 10 - } -} +{ "tool": "list-all-pages", "parameters": { "page_size": 10 } } ``` --- @@ -2426,20 +2317,16 @@ This skill provides 25 tools for notion integration. **Description**: List comments on a block or page. **Parameters**: + - **block_id** (string) **(required)**: Block or page ID to get comments for - **page_size** (number): Number of results (default 20, max 100) **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "list-comments", - "parameters": { - "block_id": "example_block_id", - "page_size": 10 - } -} +{ "tool": "list-comments", "parameters": { "block_id": "example_block_id", "page_size": 10 } } ``` --- @@ -2449,18 +2336,15 @@ This skill provides 25 tools for notion integration. **Description**: List all users in the workspace that the integration can see. **Parameters**: + - **page_size** (number): Number of results (default 20, max 100) **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "list-users", - "parameters": { - "page_size": 10 - } -} +{ "tool": "list-users", "parameters": { "page_size": 10 } } ``` --- @@ -2470,6 +2354,7 @@ This skill provides 25 tools for notion integration. **Description**: Query a database with optional filters and sorts. Returns database rows/pages. **Parameters**: + - **database_id** (string) **(required)**: The database ID to query - **filter** (string): JSON string of filter object (Notion filter syntax) - **page_size** (number): Number of results (default 20, max 100) @@ -2478,6 +2363,7 @@ This skill provides 25 tools for notion integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "query-database", @@ -2497,6 +2383,7 @@ This skill provides 25 tools for notion integration. **Description**: Search for pages and databases in your Notion workspace. Can filter by type (page or database) and returns matching results. **Parameters**: + - **filter** (string): Filter results by type - **page_size** (number): Number of results to return (default 20, max 100) - **query** (string): Search query (optional, returns recent if empty) @@ -2504,14 +2391,11 @@ This skill provides 25 tools for notion integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "search", - "parameters": { - "filter": "example_filter", - "page_size": 10, - "query": "example_query" - } + "parameters": { "filter": "example_filter", "page_size": 10, "query": "example_query" } } ``` @@ -2521,16 +2405,14 @@ This skill provides 25 tools for notion integration. **Description**: AI summarization of Notion pages is now handled by the backend server. Synced page content is submitted to the server which runs summarization. -**Parameters**: *None* +**Parameters**: _None_ **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "summarize-pages", - "parameters": {} -} +{ "tool": "summarize-pages", "parameters": {} } ``` --- @@ -2539,16 +2421,14 @@ This skill provides 25 tools for notion integration. **Description**: Trigger an immediate Notion sync to refresh local data. Returns sync results including counts of synced pages and databases. -**Parameters**: *None* +**Parameters**: _None_ **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "sync-now", - "parameters": {} -} +{ "tool": "sync-now", "parameters": {} } ``` --- @@ -2557,16 +2437,14 @@ This skill provides 25 tools for notion integration. **Description**: Get the current Notion sync status including last sync time, total synced pages/databases, sync progress, and any errors. -**Parameters**: *None* +**Parameters**: _None_ **Usage Context**: Available in all environments **Example**: + ```json -{ - "tool": "sync-status", - "parameters": {} -} +{ "tool": "sync-status", "parameters": {} } ``` --- @@ -2576,6 +2454,7 @@ This skill provides 25 tools for notion integration. **Description**: Update a block's content. The structure depends on the block type. **Parameters**: + - **archived** (string): Set to true to archive the block - **block_id** (string) **(required)**: The block ID to update - **content** (string): JSON string of the block type content. Example for paragraph: {"paragraph":{"rich_text":[{"text":{"content":"Updated text"}}]}} @@ -2583,6 +2462,7 @@ This skill provides 25 tools for notion integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "update-block", @@ -2601,6 +2481,7 @@ This skill provides 25 tools for notion integration. **Description**: Update a database's title or properties schema. **Parameters**: + - **database_id** (string) **(required)**: The database ID to update - **properties** (string): JSON string of properties to add or update - **title** (string): New title (optional) @@ -2608,6 +2489,7 @@ This skill provides 25 tools for notion integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "update-database", @@ -2626,6 +2508,7 @@ This skill provides 25 tools for notion integration. **Description**: Update a page's properties. Can update title and other properties. Use notion-append-text to add content blocks. **Parameters**: + - **archived** (string): Set to true to archive the page - **page_id** (string) **(required)**: The page ID to update - **properties** (string): JSON string of properties to update @@ -2634,6 +2517,7 @@ This skill provides 25 tools for notion integration. **Usage Context**: Available in all environments **Example**: + ```json { "tool": "update-page", @@ -2648,28 +2532,31 @@ This skill provides 25 tools for notion integration. --- - ## Tool Usage Guidelines ### Authentication + - All tools require proper authentication setup through the Skills system - OAuth credentials are managed securely and refreshed automatically - API keys are stored encrypted in the application keychain ### Rate Limiting + - Tools automatically respect API rate limits of external services - Intelligent retry logic handles temporary failures with exponential backoff ### Error Handling + - All tools return structured error responses with detailed information - Network failures trigger automatic retry with configurable attempts --- **Tool Statistics** + - Total Tools: 104 - Active Skills: 3 - Last Updated: 2026-03-06T10:39:36.767Z -*This file was automatically generated when the app loaded.* -*Tools are discovered from the running V8 skills runtime.* \ No newline at end of file +_This file was automatically generated when the app loaded._ +_Tools are discovered from the running V8 skills runtime._ diff --git a/docs/gmail-skill-integration.md b/docs/gmail-skill-integration.md new file mode 100644 index 000000000..1bee44290 --- /dev/null +++ b/docs/gmail-skill-integration.md @@ -0,0 +1,630 @@ +# Gmail Skill Integration — Design Reference + +**Scope:** Native Gmail skill — OAuth2 connect flow, tunnel-based callback, token storage, +initial inbox sync, and `send_email` tool dispatch. + +**Status:** Design reference / implementation guide. + +--- + +## 1. Overview + +This document describes the full lifecycle of connecting a Gmail skill to ZeroClaw. +It covers four concerns in order: + +1. **OAuth2 connect** — how the user authorises Gmail access +2. **Tunnel callback** — how the authorization code reaches the backend through the tunnel +3. **Token storage** — how credentials are encrypted and persisted in the auth profile store +4. **Email sync + send** — how the initial inbox is fetched and how sending works + +The skill is declared with the following manifest: + +```json +{ + "id": "gmail", + "name": "Gmail", + "version": "1.0.0", + "description": "Gmail integration via Google API — comprehensive email management with OAuth2 authentication, send/receive, labels, search, and attachments.", + "auto_start": false, + "platforms": ["windows", "macos", "linux", "android", "ios"], + "setup": { + "required": true, + "label": "Connect Gmail", + "oauth": { + "provider": "gmail", + "scopes": [ + "https://www.googleapis.com/auth/gmail.send", + "https://www.googleapis.com/auth/gmail.modify", + "https://www.googleapis.com/auth/gmail.labels" + ] + } + } +} +``` + +The equivalent `SKILL.toml` that ZeroClaw loads from +`~/.zeroclaw/workspace/skills/gmail/SKILL.toml` is shown in §2. + +--- + +## 2. Skill Manifest (`SKILL.toml`) + +Skills in ZeroClaw are loaded by `src/skills/mod.rs`. The loader reads a `SKILL.toml` +(or `SKILL.md`) file from `~/.zeroclaw/workspace/skills//`. The Rust struct it +populates is `Skill` / `SkillManifest` / `SkillTool`. + +`SKILL.toml` for Gmail: + +```toml +[skill] +name = "gmail" +version = "1.0.0" +description = "Gmail integration via Google API — send/receive, labels, search, attachments." +author = "zeroclaw" +tags = ["email", "google", "productivity"] + +# OAuth setup block — interpreted by the skill connect subsystem (see §3). +[skill.setup] +required = true +label = "Connect Gmail" + +[skill.setup.oauth] +provider = "gmail" +scopes = [ + "https://www.googleapis.com/auth/gmail.send", + "https://www.googleapis.com/auth/gmail.modify", + "https://www.googleapis.com/auth/gmail.labels", +] + +# Tools exposed to the agent after connection. + +[[tools]] +name = "gmail_send_email" +description = "Send an email via Gmail. Requires a connected Gmail account." +kind = "http" +command = "https://gmail.googleapis.com/gmail/v1/users/me/messages/send" + +[[tools]] +name = "gmail_list_messages" +description = "List messages in the Gmail inbox. Supports query filters." +kind = "http" +command = "https://gmail.googleapis.com/gmail/v1/users/me/messages" + +[[tools]] +name = "gmail_get_message" +description = "Fetch the full content of a single Gmail message by ID." +kind = "http" +command = "https://gmail.googleapis.com/gmail/v1/users/me/messages/{id}" + +[[tools]] +name = "gmail_modify_labels" +description = "Add or remove labels on a Gmail message." +kind = "http" +command = "https://gmail.googleapis.com/gmail/v1/users/me/messages/{id}/modify" +``` + +At runtime, `load_skills_with_config()` (`src/skills/mod.rs:78`) reads these tools into +`Vec` and injects them into the agent system prompt. The `kind = "http"` tools +are dispatched via `HttpRequestTool` (`src/tools/http_request.rs`) with the stored +Bearer token injected as the `Authorization` header (see §5). + +--- + +## 3. OAuth2 Connect Flow + +### 3.1 What the user triggers + +When the user asks to connect Gmail (via the frontend or a CLI command), the backend: + +1. Generates a **PKCE pair** (code verifier + SHA-256 challenge) — following the same + pattern as `src/auth/openai_oauth.rs:generate_pkce_state()`. +2. Generates a random CSRF `state` token (24 bytes, base64url). +3. Constructs the Google OAuth2 authorisation URL: + +``` +https://accounts.google.com/o/oauth2/v2/auth + ?response_type=code + &client_id= + &redirect_uri=/auth/callback/gmail + &scope=https://www.googleapis.com/auth/gmail.send + https://www.googleapis.com/auth/gmail.modify + https://www.googleapis.com/auth/gmail.labels + &code_challenge= + &code_challenge_method=S256 + &state= + &access_type=offline + &prompt=consent +``` + +`access_type=offline` is required to receive a `refresh_token` from Google. +`prompt=consent` ensures the refresh token is issued on every connect, not just the first. + +4. The URL is sent to the frontend. The frontend opens it in a browser or WebView. + +### 3.2 Where the redirect URI points + +The `redirect_uri` is the **tunnel public URL** with the path `/auth/callback/gmail`. + +ZeroClaw's tunnel system (`src/tunnel/mod.rs`) exposes the local gateway port via one of: + +| Provider | How public URL is obtained | +|---|---| +| `cloudflare` | `cloudflared tunnel` process stdout — `src/tunnel/cloudflare.rs` | +| `ngrok` | ngrok local API `GET /api/tunnels` — `src/tunnel/ngrok.rs` | +| `tailscale` | `tailscale funnel` + hostname — `src/tunnel/tailscale.rs` | +| `custom` | user-supplied URL or stdout pattern — `src/tunnel/custom.rs` | + +The tunnel's `start(local_host, local_port)` method returns the public URL string. +This URL is what gets used as the `redirect_uri`. + +Example with Cloudflare Tunnel: +``` +https://your-subdomain.trycloudflare.com/auth/callback/gmail +``` + +Google's OAuth2 server sends the browser to this URL after the user grants consent. + +--- + +## 4. Receiving the Authorization Code via the Tunnel + +### 4.1 The callback endpoint + +The gateway (`src/gateway/mod.rs`) runs as an Axum HTTP server. A new route needs to be +registered to receive the OAuth2 callback: + +``` +GET /auth/callback/gmail?code=&state= +``` + +This route handler must: + +1. **Validate the `state` parameter** against the CSRF token stored in memory when the + flow was initiated. Reject mismatches with `400 Bad Request`. +2. **Extract the `code`** query parameter. +3. **Exchange the code** for tokens by calling Google's token endpoint: + +``` +POST https://oauth2.googleapis.com/token + grant_type=authorization_code + code= + client_id= + client_secret= + redirect_uri=/auth/callback/gmail + code_verifier= +``` + +The exchange pattern mirrors `src/auth/openai_oauth.rs:exchange_code_for_tokens()`, +which posts a form body and parses the JSON response into `TokenSet`. + +Google returns: +```json +{ + "access_token": "ya29.a0...", + "expires_in": 3599, + "refresh_token": "1//0g...", + "scope": "https://www.googleapis.com/auth/gmail.send ...", + "token_type": "Bearer" +} +``` + +4. The handler stores the `TokenSet` (see §4.2) and returns a success response to the + browser (an HTML page or redirect to the frontend app confirming the connection). + +### 4.2 The in-flight CSRF / PKCE state + +Before redirecting the user to Google, the pending PKCE state and CSRF token must be +stored so the callback handler can look them up. The right storage is the skill's dedicated +in-memory or short-lived file-backed state, keyed by the `state` value: + +``` +pending_oauth_states: HashMap +``` + +Entries expire after a fixed window (e.g. 10 minutes) to prevent orphaned state +accumulation. This follows the same pattern ZeroClaw already uses for pairing nonces +in `src/security/pairing.rs`. + +--- + +## 5. Token Storage + +After a successful token exchange, the tokens are persisted using the `AuthService` +and `AuthProfilesStore` from `src/auth/`. + +### 5.1 Storing the token set + +```rust +// Pseudocode — mirrors src/auth/mod.rs AuthService::store_openai_tokens() +let token_set = TokenSet { + access_token: response.access_token, + refresh_token: response.refresh_token, + id_token: None, + expires_at: Some(Utc::now() + Duration::seconds(response.expires_in)), + token_type: Some("Bearer".into()), + scope: Some(scopes.join(" ")), +}; + +auth_service + .store_oauth_tokens("gmail", "default", token_set, None, true) + .await?; +``` + +`store_oauth_tokens` calls `AuthProfilesStore::upsert_profile()`, which: + +1. Creates or updates an `AuthProfile` with `kind = AuthProfileKind::OAuth`. +2. Serialises the profile data to `~/.zeroclaw/auth-profiles.json` (file-locked via + `auth-profiles.lock` with a 10-second timeout — `src/auth/profiles.rs:16–17`). + +### 5.2 How tokens are encrypted at rest + +Before writing to disk, every secret field passes through `SecretStore::encrypt()` +(`src/security/secrets.rs`). The encryption scheme is: + +- **Algorithm**: ChaCha20-Poly1305 AEAD (256-bit key) +- **Key file**: `~/.zeroclaw/.secret_key` (permissions 0600, created on first use) +- **Format on disk**: `enc2:` +- **Config**: encryption is enabled by default; disable with `secrets.encrypt = false` + +The stored profile entry looks like: + +```json +{ + "id": "gmail:default", + "provider": "gmail", + "profile_name": "default", + "kind": "oauth", + "token_set": { + "access_token": "enc2:a1b2c3...", + "refresh_token": "enc2:d4e5f6...", + "expires_at": "2026-03-09T12:00:00Z", + "token_type": "Bearer", + "scope": "https://www.googleapis.com/auth/gmail.send ..." + }, + "created_at": "2026-03-09T11:00:00Z", + "updated_at": "2026-03-09T11:00:00Z" +} +``` + +### 5.3 Token refresh + +Google access tokens expire after 3600 seconds. Before any Gmail API call, the skill +must check `token_set.is_expiring_within(Duration::from_secs(90))` (the same 90-second +skew used for OpenAI tokens in `src/auth/mod.rs:160`). If expiring: + +``` +POST https://oauth2.googleapis.com/token + grant_type=refresh_token + refresh_token= + client_id= + client_secret= +``` + +The new `access_token` (and new `refresh_token` if Google rotates it) is written back +to the auth profile via `AuthProfilesStore::update_profile()`. + +--- + +## 6. Initial Email Sync (First 100 Emails) + +Once connected, the skill performs a one-time initial sync. This is triggered +immediately after a successful token exchange and runs as a background task. + +### 6.1 Fetch message IDs + +``` +GET https://gmail.googleapis.com/gmail/v1/users/me/messages + ?maxResults=100 + &labelIds=INBOX +Authorization: Bearer +``` + +Returns up to 100 message descriptors: `[{ "id": "...", "threadId": "..." }, ...]`. + +### 6.2 Fetch full message details + +For each message ID, fetch the full message: + +``` +GET https://gmail.googleapis.com/gmail/v1/users/me/messages/{id} + ?format=full +Authorization: Bearer +``` + +The response contains headers (From, To, Subject, Date), a snippet, and the body parts. + +In practice, to avoid 100 sequential requests, use the Gmail batch API: + +``` +POST https://www.googleapis.com/batch/gmail/v1 +Content-Type: multipart/mixed; boundary="batch_boundary" + +--batch_boundary +Content-Type: application/http +GET /gmail/v1/users/me/messages/{id1}?format=full +... +``` + +### 6.3 Storing emails in ZeroClaw memory + +Each email is stored via `memory_store` (`src/tools/memory_store.rs`) with: + +| Field | Value | +|---|---| +| `key` | `gmail_msg_` | +| `content` | Formatted string: `From: ... Subject: ... Date: ... Snippet: ...` | +| `category` | `MemoryCategory::Custom("gmail")` | + +This makes every synced email searchable via `memory_recall` with queries like +`"gmail from:zeroclaw@example.com"`. + +For larger body content that exceeds a single memory entry, store the full body as a +separate entry keyed `gmail_body_` and the summary/metadata as +`gmail_msg_`. + +Example stored entries after sync: + +``` +key: gmail_msg_18de7f2a1b3c4e5d +content: From: sender@example.com + To: me@gmail.com + Subject: Project update + Date: 2026-03-08T14:32:00Z + Snippet: "Here is the latest update on the project..." + Labels: INBOX, UNREAD +category: gmail + +key: gmail_body_18de7f2a1b3c4e5d +content: (full decoded email body text) +category: gmail +``` + +### 6.4 Sync state tracking + +Store the highest-known history ID after sync so incremental sync can pick up from +where it left off: + +``` +key: gmail_sync_history_id +content: 12345678 +category: core +``` + +--- + +## 7. The `gmail_send_email` Tool + +### 7.1 Tool definition in `SKILL.toml` + +The `[[tools]]` entry from §2: + +```toml +[[tools]] +name = "gmail_send_email" +description = "Send an email via Gmail. Requires a connected Gmail account." +kind = "http" +command = "https://gmail.googleapis.com/gmail/v1/users/me/messages/send" +``` + +### 7.2 How the agent calls it + +The LLM emits a tool call: + +```json +{ + "name": "gmail_send_email", + "arguments": { + "to": "recipient@example.com", + "subject": "Hello from ZeroClaw", + "body": "This is a test email sent by the agent." + } +} +``` + +### 7.3 Tool dispatch path + +``` +Agent loop (src/agent/loop_.rs) + └── dispatches to registered tool by name + └── HttpRequestTool::execute(args) [src/tools/http_request.rs] + ├── Retrieve gmail access_token from AuthService + │ └── auth_service.get_provider_bearer_token("gmail", None) + │ └── decrypts enc2: value via SecretStore::decrypt() + ├── Build RFC 2822 message and base64url-encode it + ├── POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send + │ Headers: + │ Authorization: Bearer + │ Content-Type: application/json + │ Body: + │ { "raw": "" } + └── Return ToolResult { success, output: "Message sent. ID: " } +``` + +### 7.4 Building the RFC 2822 message + +Gmail's send endpoint requires the email encoded as an RFC 2822 message, then base64url +encoded. The structure: + +``` +From: me@gmail.com +To: recipient@example.com +Subject: Hello from ZeroClaw +Content-Type: text/plain; charset="UTF-8" +MIME-Version: 1.0 + +This is a test email sent by the agent. +``` + +Then the entire string is base64url-encoded (no padding) and placed in the `raw` field: + +```json +{ + "raw": "RnJvbTogbWVAZ21haWwuY29tCi..." +} +``` + +### 7.5 Security enforcement + +Before executing any outbound HTTP call, the `SecurityPolicy` is consulted: + +```rust +// src/security/policy.rs +security.enforce_tool_operation(ToolOperation::Act, "gmail_send_email") +``` + +`ToolOperation::Act` is the category for write/side-effect operations. If the agent is +running in `ReadOnly` or `Supervised` mode, this call fails with an explicit error before +any network request is made. + +The `HttpRequestTool` additionally enforces the `http.allowed_domains` allowlist. For +Gmail tools, `googleapis.com` must be present in that list: + +```toml +# config.toml +[http] +enabled = true +allowed_domains = ["googleapis.com"] +``` + +### 7.6 Reply-to and threading + +To send a reply within an existing thread, add the Gmail `threadId` to the request body: + +```json +{ + "raw": "", + "threadId": "18de7f2a1b3c4e5d" +} +``` + +The RFC 2822 message must include `In-Reply-To: ` and +`References: ` headers for proper threading. + +--- + +## 8. Full End-to-End Sequence + +``` +Frontend / User + │ + │ 1. "Connect Gmail" + ▼ +ZeroClaw Backend + │ 2. Generate PKCE + CSRF state + │ 3. Build Google OAuth2 authorize URL with tunnel redirect_uri + │ 4. Return URL to frontend + │ + ▼ +Browser (user) + │ 5. User visits URL, grants consent in Google + │ + ▼ +Google OAuth2 + │ 6. Browser redirected to /auth/callback/gmail?code=XXX&state=YYY + │ + ▼ +Tunnel (cloudflare / ngrok / tailscale) + │ 7. Proxies HTTPS request to local gateway port + │ + ▼ +Gateway (src/gateway/mod.rs) — GET /auth/callback/gmail + │ 8. Validate CSRF state + │ 9. Exchange code → TokenSet via POST https://oauth2.googleapis.com/token + │ 10. Store TokenSet in AuthProfilesStore (encrypted, "gmail:default") + │ 11. Return success page/redirect to frontend + │ + ▼ +Background task — initial sync + │ 12. GET /gmail/v1/users/me/messages?maxResults=100&labelIds=INBOX + │ 13. Batch-fetch 100 full messages + │ 14. memory_store each email under key=gmail_msg_, category=gmail + │ 15. memory_store gmail_sync_history_id = + │ + ▼ +Agent (subsequent interactions) + │ 16. LLM generates tool call: gmail_send_email { to, subject, body } + │ 17. Agent loop dispatches to HttpRequestTool + │ 18. HttpRequestTool retrieves + decrypts access_token from AuthService + │ 19. Refreshes token if expiring within 90 seconds + │ 20. POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send + │ 21. Returns ToolResult to agent + ▼ +Done +``` + +--- + +## 9. Configuration Reference + +All Gmail skill config lives in `config.toml`: + +```toml +# Enable HTTP request tool with Google API access +[http] +enabled = true +allowed_domains = ["googleapis.com", "oauth2.googleapis.com"] +timeout_secs = 30 +max_response_size = 524288 # 512KB — enough for email batch responses + +# Tunnel for OAuth callback (pick one) +[tunnel] +provider = "cloudflare" +[tunnel.cloudflare] +token = "your-cloudflare-tunnel-token" + +# Or ngrok: +# [tunnel] +# provider = "ngrok" +# [tunnel.ngrok] +# auth_token = "your-ngrok-token" + +# Google OAuth2 app credentials (stored encrypted) +# Set via environment or onboard wizard — never commit raw values +[integrations.gmail] +client_id = "enc2:..." +client_secret = "enc2:..." +``` + +The Google credentials (`client_id`, `client_secret`) are encrypted by `SecretStore` +before being written to `config.toml` — the same ChaCha20-Poly1305 scheme used for all +secrets (`src/security/secrets.rs`). + +--- + +## 10. Key Source Files + +| File | Role | +|---|---| +| `src/skills/mod.rs` | Skill loading, `SkillTool` struct, `load_skills_with_config()` | +| `src/auth/mod.rs` | `AuthService` — store/retrieve/refresh OAuth tokens | +| `src/auth/profiles.rs` | `AuthProfile`, `TokenSet`, `AuthProfilesStore` — JSON persistence | +| `src/auth/openai_oauth.rs` | PKCE generation, code exchange, refresh — reference pattern | +| `src/security/secrets.rs` | `SecretStore` — ChaCha20-Poly1305 encrypt/decrypt | +| `src/tunnel/mod.rs` | `Tunnel` trait + factory — public URL for OAuth redirect | +| `src/tunnel/cloudflare.rs` | Cloudflare Tunnel implementation | +| `src/tunnel/ngrok.rs` | ngrok implementation | +| `src/gateway/mod.rs` | Axum HTTP gateway — where `/auth/callback/gmail` is registered | +| `src/tools/http_request.rs` | `HttpRequestTool` — dispatches Gmail API calls | +| `src/tools/memory_store.rs` | `MemoryStoreTool` — stores synced emails | +| `src/tools/memory_recall.rs` | `MemoryRecallTool` — searches synced emails | +| `src/security/policy.rs` | `SecurityPolicy` — enforces `ToolOperation::Act` guards | +| `docs/config-reference.md` | Full config schema including `[http]`, `[tunnel]` | + +--- + +## 11. Security Notes + +- The OAuth2 `redirect_uri` **must be the tunnel URL**. It cannot be `localhost` in a + remote/mobile scenario. Google validates the exact URI registered in the Google Cloud + Console; register it as `https:///auth/callback/gmail`. +- The CSRF `state` token must be validated on every callback. Reject mismatched or + missing state with `400 Bad Request` before touching any tokens. +- The PKCE verifier must be destroyed after a successful or failed exchange — never + persist it beyond the in-flight flow. +- `client_secret` must never appear in plaintext in `config.toml`, logs, or agent + tool output. Encrypt it via the secret store and redact it in observability output. +- `googleapis.com` must be explicitly present in `http.allowed_domains` — the + `HttpRequestTool` enforces this allowlist before every request + (`src/tools/http_request.rs`). +- Token refresh runs under the same per-profile Tokio mutex used for OpenAI tokens + (`src/auth/mod.rs:287`) to prevent double-refresh races. diff --git a/package.json b/package.json index 6343da0c3..4a6c16f2f 100644 --- a/package.json +++ b/package.json @@ -6,14 +6,15 @@ "scripts": { "dev": "vite", "dev:web": "vite", - "dev:app": "source scripts/load-dotenv.sh && tauri dev", + "dev:app": "source scripts/load-dotenv.sh 2>/dev/null; (cd src-tauri && bash scripts/download-tdlib.sh) && LOCAL_TDLIB_PATH=$(pwd)/src-tauri/tdlib-cache/macos-$(uname -m | sed 's/arm64/aarch64/; s/x86_64/x86_64/') tauri dev", "build": "tsc && vite build", "build:app": "yarn skills:build && yarn tools:generate && tsc && vite build", "compile": "tsc --noEmit", "preview": "vite preview", "tauri": "tauri", + "tauri:dev": "source scripts/load-dotenv.sh 2>/dev/null; (cd src-tauri && bash scripts/download-tdlib.sh) && LOCAL_TDLIB_PATH=$(pwd)/src-tauri/tdlib-cache/macos-$(uname -m | sed 's/arm64/aarch64/; s/x86_64/x86_64/') tauri dev", "macos:build:debug": "yarn macos:build:release --debug", - "macos:build:release": "source scripts/load-dotenv.sh && tauri build --bundles app dmg", + "macos:build:release": "source scripts/load-dotenv.sh 2>/dev/null; (cd src-tauri && bash scripts/download-tdlib.sh) && LOCAL_TDLIB_PATH=$(pwd)/src-tauri/tdlib-cache/macos-$(uname -m | sed 's/arm64/aarch64/; s/x86_64/x86_64/') tauri build --bundles app dmg", "macos:build:sign:release": "source scripts/load-env.sh && tauri build --bundles app dmg", "macos:run": "open 'src-tauri/target/debug/bundle/macos/AlphaHuman.app'", "macos:dev": "yarn macos:build:debug && open 'src-tauri/target/debug/bundle/macos/AlphaHuman.app'", diff --git a/src-tauri/.gitignore b/src-tauri/.gitignore index 28b36d3b1..ef21e7a7a 100644 --- a/src-tauri/.gitignore +++ b/src-tauri/.gitignore @@ -12,3 +12,5 @@ # TDLib built from source (see scripts/build-tdlib-from-source.sh) /tdlib-local/ /tdlib-build/ +# TDLib downloaded by scripts/download-tdlib.sh (avoids build timeout) +/tdlib-cache/ diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 6cd3c8413..9c3fd8f0a 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -311,15 +311,6 @@ dependencies = [ "object 0.37.3", ] -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" -dependencies = [ - "derive_arbitrary", -] - [[package]] name = "archery" version = "1.2.2" @@ -818,7 +809,7 @@ dependencies = [ "arrayvec", "cc", "cfg-if", - "constant_time_eq 0.4.2", + "constant_time_eq", "cpufeatures", ] @@ -945,25 +936,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3" -[[package]] -name = "bzip2" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" -dependencies = [ - "bzip2-sys", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.13+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" -dependencies = [ - "cc", - "pkg-config", -] - [[package]] name = "cairo-rs" version = "0.18.5" @@ -1275,12 +1247,6 @@ dependencies = [ "typewit", ] -[[package]] -name = "constant_time_eq" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" - [[package]] name = "constant_time_eq" version = "0.4.2" @@ -1409,21 +1375,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - [[package]] name = "crc32fast" version = "1.5.0" @@ -1716,12 +1667,6 @@ dependencies = [ "regex", ] -[[package]] -name = "deflate64" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26bf8fc351c5ed29b5c2f0cbbac1b209b74f60ecd62e675a998df72c49af5204" - [[package]] name = "deku" version = "0.18.1" @@ -1792,17 +1737,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "derive_arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - [[package]] name = "derive_more" version = "0.99.20" @@ -3388,11 +3322,9 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2", - "system-configuration", "tokio", "tower-service", "tracing", - "windows-registry", ] [[package]] @@ -4212,27 +4144,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" -[[package]] -name = "lzma-rs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" -dependencies = [ - "byteorder", - "crc", -] - -[[package]] -name = "lzma-sys" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "mac" version = "0.1.1" @@ -6739,7 +6650,6 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64 0.22.1", "bytes", - "encoding_rs", "futures-channel", "futures-core", "futures-util", @@ -6753,7 +6663,6 @@ dependencies = [ "hyper-util", "js-sys", "log", - "mime", "mime_guess", "native-tls", "percent-encoding", @@ -7961,27 +7870,6 @@ dependencies = [ "libc", ] -[[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "system-deps" version = "6.2.2" @@ -8412,13 +8300,11 @@ dependencies = [ "futures-channel", "log", "once_cell", - "reqwest", "serde", "serde_json", "serde_with", "tdlib-rs-gen", "tdlib-rs-parser", - "zip", ] [[package]] @@ -10686,15 +10572,6 @@ version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" -[[package]] -name = "xz2" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" -dependencies = [ - "lzma-sys", -] - [[package]] name = "yoke" version = "0.7.5" @@ -10919,36 +10796,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "zip" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" -dependencies = [ - "aes", - "arbitrary", - "bzip2", - "constant_time_eq 0.3.1", - "crc32fast", - "crossbeam-utils", - "deflate64", - "displaydoc", - "flate2", - "getrandom 0.3.4", - "hmac", - "indexmap 2.13.0", - "lzma-rs", - "memchr", - "pbkdf2", - "sha1", - "thiserror 2.0.18", - "time", - "xz2", - "zeroize", - "zopfli", - "zstd", -] - [[package]] name = "zlib-rs" version = "0.5.5" @@ -10961,46 +10808,6 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439" -[[package]] -name = "zopfli" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" -dependencies = [ - "bumpalo", - "crc32fast", - "log", - "simd-adler32", -] - -[[package]] -name = "zstd" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.16+zstd.1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" -dependencies = [ - "cc", - "pkg-config", -] - [[package]] name = "zvariant" version = "5.9.2" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 18d1444f4..c0e0f201f 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -165,14 +165,13 @@ tauri-plugin-notification = "2" # Only available on desktop - prebuilt binaries don't exist for Android/iOS rquickjs = { version = "0.11", features = ["futures", "macro", "loader", "parallel"] } -# TDLib Rust bindings (desktop only - downloads prebuilt TDLib for linking) -# On macOS, the bundled dylib is replaced with a source-built version (see build.rs) -# to ensure macOS 10.15 deployment target compatibility. -tdlib-rs = { version = "1.2", features = ["download-tdlib"] } +# TDLib Rust bindings (desktop only - uses local prebuilt via LOCAL_TDLIB_PATH) +# Run: cd src-tauri && ./scripts/download-tdlib.sh (once, to populate tdlib-cache/) +tdlib-rs = { version = "1.2", features = ["local-tdlib"] } [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.build-dependencies] -# TDLib build configuration (download-tdlib for all desktop platforms) -tdlib-rs = { version = "1.2", features = ["download-tdlib"] } +# TDLib build (local-tdlib avoids reqwest timeout during Cargo build) +tdlib-rs = { version = "1.2", features = ["local-tdlib"] } [dev-dependencies] tempfile = "3" diff --git a/src-tauri/build.rs b/src-tauri/build.rs index 9e52ed211..b129988e2 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -48,7 +48,34 @@ fn maybe_override_tauri_config_for_tests() { #[cfg(not(any(target_os = "android", target_os = "ios")))] fn setup_tdlib() { - // download-tdlib: downloads prebuilt TDLib and configures linker flags + // local-tdlib: use LOCAL_TDLIB_PATH or auto-detect tdlib-cache/ + if std::env::var("LOCAL_TDLIB_PATH").is_err() { + let manifest_dir = + PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR")); + let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_else(|_| "aarch64".into()); + let arch_name = if arch == "aarch64" { "aarch64" } else { "x86_64" }; + let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_else(|_| "macos".into()); + let os_arch = if os == "macos" || os == "darwin" { + format!("macos-{arch_name}") + } else if os == "linux" { + format!("linux-{arch_name}") + } else if os == "windows" { + format!("windows-{arch_name}") + } else { + arch_name.to_string() + }; + let default = manifest_dir.join("tdlib-cache").join(&os_arch); + if default.join("lib").exists() { + std::env::set_var("LOCAL_TDLIB_PATH", &default); + } else { + panic!( + "LOCAL_TDLIB_PATH not set and tdlib-cache not found at {}.\n\ + Run: cd src-tauri && ./scripts/download-tdlib.sh\n\ + Then retry the build.", + default.display() + ); + } + } tdlib_rs::build::build(None); // On macOS, replace the bundled dylib with our source-built version @@ -105,14 +132,16 @@ fn copy_local_tdlib_for_bundle() { println!("cargo:warning=Using locally-built TDLib from {}", local.display()); local } else { - // 3. Fall back to the download-tdlib output - println!( - "cargo:warning=No source-built TDLib found for macos-{arch_name}. \ - The prebuilt TDLib will be bundled instead (targets macOS 14.0+). \ - Run: cd src-tauri && ./scripts/build-tdlib-from-source.sh" - ); - let out_dir = env::var("OUT_DIR").unwrap(); - PathBuf::from(&out_dir).join("tdlib").join("lib").join(&dylib_name) + // 3. Fall back to tdlib-cache (from download-tdlib.sh) or LOCAL_TDLIB_PATH + let local_tdlib = env::var("LOCAL_TDLIB_PATH") + .map(PathBuf::from) + .unwrap_or_else(|_| { + manifest_dir + .join("tdlib-cache") + .join(format!("macos-{arch_name}")) + }); + println!("cargo:warning=Using TDLib from {}", local_tdlib.display()); + local_tdlib.join("lib").join(&dylib_name) }; if !src_dylib.exists() { diff --git a/src-tauri/scripts/download-tdlib.sh b/src-tauri/scripts/download-tdlib.sh new file mode 100755 index 000000000..113a1b124 --- /dev/null +++ b/src-tauri/scripts/download-tdlib.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +# +# Download prebuilt TDLib from tdlib-rs releases using curl (avoids reqwest +# timeout during Cargo build). Extracts to tdlib-cache/ for use with local-tdlib. +# +# Usage: +# cd src-tauri +# ./scripts/download-tdlib.sh +# +# Then run builds with LOCAL_TDLIB_PATH set, or use yarn tauri:dev / yarn dev:app. +# + +set -euo pipefail + +TDLIB_VERSION="1.8.29" +TDLIB_RS_VERSION="1.2.0" +BASE_URL="https://github.com/FedericoBruzzone/tdlib-rs/releases/download" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +SRC_TAURI_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +CACHE_DIR="${SRC_TAURI_DIR}/tdlib-cache" + +# Detect target (CARGO_CFG_* set during cargo build; uname when run manually) +RAW_OS="${CARGO_CFG_TARGET_OS:-$(uname -s | tr '[:upper:]' '[:lower:]')}" +ARCH="${CARGO_CFG_TARGET_ARCH:-$(uname -m)}" +case "$RAW_OS" in + darwin|macos) OS_NAME="macos" ;; + linux) OS_NAME="linux" ;; + windows) OS_NAME="windows" ;; + *) OS_NAME="$RAW_OS" ;; +esac +case "$ARCH" in + arm64|aarch64) ARCH_NAME="aarch64" ;; + x86_64) ARCH_NAME="x86_64" ;; + *) ARCH_NAME="$ARCH" ;; +esac + +ZIP_NAME="tdlib-${TDLIB_VERSION}-${OS_NAME}-${ARCH_NAME}.zip" +URL="${BASE_URL}/v${TDLIB_RS_VERSION}/${ZIP_NAME}" +EXTRACT_TO="${CACHE_DIR}/${OS_NAME}-${ARCH_NAME}" + +if [ -f "${EXTRACT_TO}/lib/libtdjson.${TDLIB_VERSION}.dylib" ] || \ + [ -f "${EXTRACT_TO}/lib/libtdjson.so.${TDLIB_VERSION}" ] || \ + [ -f "${EXTRACT_TO}/lib/tdjson.lib" ]; then + echo "TDLib already cached at ${EXTRACT_TO}" + echo "Set: export LOCAL_TDLIB_PATH=${EXTRACT_TO}" + exit 0 +fi + +echo "Downloading TDLib ${TDLIB_VERSION} for ${OS_NAME}-${ARCH_NAME}..." +mkdir -p "${CACHE_DIR}" +cd "${CACHE_DIR}" + +# curl with retries and 5min timeout to avoid network issues +if ! curl -fSL --retry 3 --retry-delay 10 --connect-timeout 30 --max-time 300 \ + -o "${ZIP_NAME}" "${URL}"; then + echo "Failed to download ${URL}" + echo "Try manually: curl -fSL -o ${ZIP_NAME} '${URL}'" + exit 1 +fi + +echo "Extracting..." +unzip -o -q "${ZIP_NAME}" +rm -f "${ZIP_NAME}" + +# Zip may extract to tdlib-{version}-{os}-{arch}/ or tdlib/; normalize path +for dir in "tdlib-${TDLIB_VERSION}-${OS_NAME}-${ARCH_NAME}" "tdlib"; do + if [ -d "$dir" ]; then + rm -rf "${EXTRACT_TO}" + mv "$dir" "${EXTRACT_TO}" + break + fi +done + +if [ ! -d "${EXTRACT_TO}/lib" ]; then + echo "Error: expected lib/ not found after extract" + exit 1 +fi + +echo "TDLib cached at ${EXTRACT_TO}" +echo "Set: export LOCAL_TDLIB_PATH=${EXTRACT_TO}" diff --git a/src/components/oauth/OAuthProviderButton.tsx b/src/components/oauth/OAuthProviderButton.tsx index d98583674..85907d50b 100644 --- a/src/components/oauth/OAuthProviderButton.tsx +++ b/src/components/oauth/OAuthProviderButton.tsx @@ -1,8 +1,9 @@ import { useState } from 'react'; + import type { OAuthProviderConfig } from '../../types/oauth'; +import { IS_DEV } from '../../utils/config'; import { openUrl } from '../../utils/openUrl'; import { isTauri } from '../../utils/tauriCommands'; -import { IS_DEV } from '../../utils/config'; interface OAuthProviderButtonProps { provider: OAuthProviderConfig; @@ -25,7 +26,9 @@ const OAuthProviderButton = ({ if (IS_DEV) { console.log(`[dev] OAuth debug mode enabled. OAuth URL: ${provider.loginUrl}`); console.log('[dev] In debug mode, OAuth will return JSON response instead of redirect.'); - console.log('[dev] After OAuth completion, copy the loginToken and use: window.__simulateDeepLink("alphahuman://auth?token=YOUR_TOKEN")'); + console.log( + '[dev] After OAuth completion, copy the loginToken and use: window.__simulateDeepLink("alphahuman://auth?token=YOUR_TOKEN")' + ); } setIsLoading(true); @@ -51,18 +54,17 @@ const OAuthProviderButton = ({ ); }; -export default OAuthProviderButton; \ No newline at end of file +export default OAuthProviderButton; diff --git a/src/components/oauth/providerConfigs.tsx b/src/components/oauth/providerConfigs.tsx index 6f4766f00..34227e34f 100644 --- a/src/components/oauth/providerConfigs.tsx +++ b/src/components/oauth/providerConfigs.tsx @@ -7,28 +7,40 @@ import { BACKEND_URL, IS_DEV } from '../../utils/config'; // Provider Icons const GoogleIcon = ({ className = '' }: { className?: string }) => ( - - - - + + + + ); const TwitterIcon = ({ className = '' }: { className?: string }) => ( - + ); const GitHubIcon = ({ className = '' }: { className?: string }) => ( - + ); const DiscordIcon = ({ className = '' }: { className?: string }) => ( - + ); @@ -73,4 +85,4 @@ export const oauthProviderConfigs: OAuthProviderConfig[] = [ export const getProviderConfig = (provider: string): OAuthProviderConfig | undefined => { return oauthProviderConfigs.find(config => config.id === provider); -}; \ No newline at end of file +}; diff --git a/src/lib/skills/manager.ts b/src/lib/skills/manager.ts index f407ad6bf..898b3e8ac 100644 --- a/src/lib/skills/manager.ts +++ b/src/lib/skills/manager.ts @@ -226,11 +226,15 @@ class SkillManager { name: string, args: Record, ): Promise<{ content: Array<{ type: string; text: string }>; isError: boolean }> { + console.log(`[SkillManager] callTool skill="${skillId}" tool="${name}"`); const runtime = this.runtimes.get(skillId); if (!runtime) { + console.error(`[SkillManager] callTool failed — skill "${skillId}" has no running runtime`); throw new Error(`Skill ${skillId} is not running`); } - return runtime.callTool(name, args); + const result = await runtime.callTool(name, args); + console.log(`[SkillManager] callTool result skill="${skillId}" tool="${name}" isError=${result.isError}`); + return result; } /** diff --git a/src/lib/skills/runtime.ts b/src/lib/skills/runtime.ts index ac1556321..89fe7394e 100644 --- a/src/lib/skills/runtime.ts +++ b/src/lib/skills/runtime.ts @@ -141,7 +141,10 @@ export class SkillRuntime { content: Array<{ type: string; text: string }>; isError: boolean; }> { - return this.transport.request("tools/call", { name, arguments: args }); + console.log(`[SkillRuntime] callTool skill="${this.manifest.id}" tool="${name}"`); + const result = await this.transport.request<{ content: Array<{ type: string; text: string }>; isError: boolean }>("tools/call", { name, arguments: args }); + console.log(`[SkillRuntime] tools/call response skill="${this.manifest.id}" tool="${name}" isError=${result.isError}`); + return result; } /** diff --git a/src/lib/skills/transport.ts b/src/lib/skills/transport.ts index 4f8c1fad5..971996f0d 100644 --- a/src/lib/skills/transport.ts +++ b/src/lib/skills/transport.ts @@ -51,11 +51,7 @@ export class SkillTransport { throw new Error("Skill transport not started"); } - console.log("[skill-transport] Sending request", { - skillId: this.skillId, - method, - hasParams: params !== undefined, - }); + console.log("[skill-transport] →", { skillId: this.skillId, method, params }); const result = await invoke("runtime_rpc", { skillId: this.skillId, @@ -63,10 +59,7 @@ export class SkillTransport { params: params ?? {}, }); - console.debug("[skill-transport] Received response", { - skillId: this.skillId, - method, - }); + console.log("[skill-transport] ←", { skillId: this.skillId, method, result }); return result; } diff --git a/src/pages/Conversations.tsx b/src/pages/Conversations.tsx index 79a19f0fe..54cf0e8df 100644 --- a/src/pages/Conversations.tsx +++ b/src/pages/Conversations.tsx @@ -9,9 +9,15 @@ import { import Markdown from 'react-markdown'; import { useNavigate, useParams } from 'react-router-dom'; -import { inferenceApi, type ModelInfo } from '../services/api/inferenceApi'; import { injectAll } from '../lib/ai/injector'; import type { Message } from '../lib/ai/providers/interface'; +import { skillManager } from '../lib/skills/manager'; +import { + type ChatMessage, + inferenceApi, + type ModelInfo, + type Tool, +} from '../services/api/inferenceApi'; import { useAppDispatch, useAppSelector } from '../store/hooks'; import { addInferenceResponse, @@ -63,6 +69,8 @@ const Conversations = () => { isLoadingSuggestions, } = useAppSelector(state => state.thread); + const skillsState = useAppSelector(state => state.skills); + const [showPurgeConfirm, setShowPurgeConfirm] = useState(false); const [confirmDeleteId, setConfirmDeleteId] = useState(null); const [inputValue, setInputValue] = useState(''); @@ -255,14 +263,11 @@ const Conversations = () => { // Process user message with SOUL + TOOLS injection let processedUserContent = trimmed; try { - const userMessage: Message = { - role: 'user', - content: [{ type: 'text', text: trimmed }] - }; + const userMessage: Message = { role: 'user', content: [{ type: 'text', text: trimmed }] }; const injectedMessage = await injectAll(userMessage, { mode: 'context-block', - includeMetadata: false + includeMetadata: false, }); // Extract the processed text @@ -277,7 +282,7 @@ const Conversations = () => { // Continue with original message } - const chatMessages = [ + const chatMessages: ChatMessage[] = [ ...historySnapshot.map(m => ({ role: (m.sender === 'user' ? 'user' : 'assistant') as 'user' | 'assistant', content: m.content, @@ -285,13 +290,105 @@ const Conversations = () => { { role: 'user' as const, content: processedUserContent }, ]; - const response = await inferenceApi.createChatCompletion({ - model: selectedModel, - messages: chatMessages, - }); + // Build tool definitions for ALL ready skills — namespaced as {skillId}__{toolName} + const allSkillTools: Tool[] = Object.entries(skillsState.skills) + .filter(([, skill]) => skill.status === 'ready' && skill.tools?.length) + .flatMap(([skillId, skill]) => + (skill.tools ?? []).map(t => ({ + type: 'function' as const, + function: { + name: `${skillId}__${t.name}`, + description: t.description, + parameters: t.inputSchema as Tool['function']['parameters'], + }, + })) + ); - const content = response.choices[0]?.message?.content ?? ''; - dispatch(addInferenceResponse({ content })); + console.log( + `[Conversations] active skill tools: ${allSkillTools.length}`, + allSkillTools.map(t => t.function.name) + ); + + // Agentic tool calling loop — handles multi-turn tool execution + const loopMessages = [...chatMessages]; + let finalContent = ''; + const MAX_TOOL_ROUNDS = 5; + + for (let round = 0; round < MAX_TOOL_ROUNDS; round++) { + const response = await inferenceApi.createChatCompletion({ + model: selectedModel, + messages: loopMessages, + ...(allSkillTools.length > 0 ? { tools: allSkillTools, tool_choice: 'auto' } : {}), + }); + console.log('🚀 ~ handleSendMessage ~ response:', response); + + const choice = response.choices[0]; + if (!choice) break; + + const { finish_reason, message } = choice; + + if (finish_reason === 'tool_calls' && message.tool_calls?.length) { + // Append assistant message with tool_calls + loopMessages.push({ + role: 'assistant', + content: message.content ?? '', + tool_calls: message.tool_calls, + }); + + // Execute each tool and collect results + for (const tc of message.tool_calls) { + const dunderIdx = tc.function.name.indexOf('__'); + const skillId = dunderIdx !== -1 ? tc.function.name.substring(0, dunderIdx) : ''; + const toolName = + dunderIdx !== -1 ? tc.function.name.substring(dunderIdx + 2) : tc.function.name; + + console.log( + `[Conversations] tool_call dispatched — skill="${skillId}" tool="${toolName}" call_id="${tc.id}"` + ); + + let toolResultContent = ''; + try { + let toolArgs: Record = {}; + try { + toolArgs = JSON.parse(tc.function.arguments) as Record; + } catch { + toolArgs = {}; + } + console.log( + `[Conversations] calling skillManager.callTool("${skillId}", "${toolName}")`, + toolArgs + ); + const result = await skillManager.callTool(skillId, toolName, toolArgs); + toolResultContent = result.content.map(c => c.text).join('\n'); + if (result.isError) { + console.warn( + `[Conversations] tool "${toolName}" returned an error:`, + toolResultContent + ); + toolResultContent = `Error: ${toolResultContent}`; + } else { + console.log( + `[Conversations] tool "${toolName}" succeeded:`, + toolResultContent.slice(0, 200) + ); + } + } catch (toolErr) { + console.error(`[Conversations] tool "${toolName}" threw:`, toolErr); + toolResultContent = `Tool execution failed: ${toolErr instanceof Error ? toolErr.message : String(toolErr)}`; + } + + loopMessages.push({ role: 'tool', tool_call_id: tc.id, content: toolResultContent }); + } + // Continue loop to get final response after tool results + continue; + } + + // Normal (non-tool) response — done + finalContent = message.content ?? ''; + break; + } + + dispatch(addInferenceResponse({ content: finalContent })); } catch (err) { dispatch(removeOptimisticMessages()); const msg = diff --git a/src/services/api/inferenceApi.ts b/src/services/api/inferenceApi.ts index 2c59841cc..070e619ad 100644 --- a/src/services/api/inferenceApi.ts +++ b/src/services/api/inferenceApi.ts @@ -2,11 +2,41 @@ import { apiClient } from '../apiClient'; // ── Request types ──────────────────────────────────────────────────────────── -export type ChatRole = 'system' | 'user' | 'assistant'; +export type ChatRole = 'system' | 'user' | 'assistant' | 'tool'; export interface ChatMessage { role: ChatRole; content: string; + /** tool_call_id for role=tool messages */ + tool_call_id?: string; + /** tool_calls emitted by the assistant */ + tool_calls?: ToolCall[]; +} + +// ── Tool calling types (OpenAI-compatible) ─────────────────────────────────── + +export interface ToolFunction { + name: string; + description: string; + parameters: { + type: 'object'; + properties: Record; + required?: string[]; + }; +} + +export interface Tool { + type: 'function'; + function: ToolFunction; +} + +export interface ToolCall { + id: string; + type: 'function'; + function: { + name: string; + arguments: string; + }; } export interface ChatCompletionRequest { @@ -15,6 +45,8 @@ export interface ChatCompletionRequest { stream?: boolean; temperature?: number; max_tokens?: number; + tools?: Tool[]; + tool_choice?: 'none' | 'auto' | 'required'; } export interface TextCompletionRequest { @@ -41,7 +73,7 @@ export interface ModelsListResponse { export interface ChatCompletionChoice { index: number; - message: ChatMessage; + message: ChatMessage & { tool_calls?: ToolCall[] }; finish_reason: string | null; } diff --git a/src/services/daemonHealthService.ts b/src/services/daemonHealthService.ts index e619c0d26..8d009f890 100644 --- a/src/services/daemonHealthService.ts +++ b/src/services/daemonHealthService.ts @@ -27,8 +27,8 @@ export class DaemonHealthService { async setupHealthListener(): Promise { console.log('[DaemonHealth] setupHealthListener() called - starting setup process'); try { - console.log('[DaemonHealth] About to call listen() for alphahuman:health event'); - console.log('[DaemonHealth] Setting up alphahuman:health event listener'); + // console.log('[DaemonHealth] About to call listen() for alphahuman:health event'); + // console.log('[DaemonHealth] Setting up alphahuman:health event listener'); this.healthEventListener = await listen('alphahuman:health', event => { console.log('[DaemonHealth] Received health event:', event.payload); @@ -44,11 +44,11 @@ export class DaemonHealthService { console.log('[DaemonHealth] alphahuman:health listener created successfully'); // Start initial timeout - console.log('[DaemonHealth] Starting health timeout'); + // console.log('[DaemonHealth] Starting health timeout'); this.startHealthTimeout(); - console.log('[DaemonHealth] Health timeout started'); + // console.log('[DaemonHealth] Health timeout started'); - console.log('[DaemonHealth] Health listener setup complete'); + // console.log('[DaemonHealth] Health listener setup complete'); return this.healthEventListener; } catch (error) { console.error('[DaemonHealth] Failed to setup health listener:', error); @@ -147,7 +147,7 @@ export class DaemonHealthService { // Update the health snapshot in Redux store.dispatch(updateHealthSnapshot({ userId, healthSnapshot: snapshot })); - console.log('[DaemonHealth] Updated health snapshot for user:', userId, snapshot); + // console.log('[DaemonHealth] Updated health snapshot for user:', userId, snapshot); } catch (error) { console.error('[DaemonHealth] Error updating Redux from health:', error); } @@ -175,12 +175,7 @@ export class DaemonHealthService { }, this.HEALTH_TIMEOUT_MS); // Store timeout ID in Redux for cleanup - store.dispatch( - setHealthTimeoutId({ - userId, - timeoutId: this.healthTimeoutId.toString(), - }) - ); + store.dispatch(setHealthTimeoutId({ userId, timeoutId: this.healthTimeoutId.toString() })); } /** @@ -204,4 +199,4 @@ export class DaemonHealthService { } // Export singleton instance -export const daemonHealthService = new DaemonHealthService(); \ No newline at end of file +export const daemonHealthService = new DaemonHealthService();