diff --git a/ai/TOOLS.md b/ai/TOOLS.md index 06f9c42e3..74c040ea1 100644 --- a/ai/TOOLS.md +++ b/ai/TOOLS.md @@ -1,1995 +1,93 @@ # AlphaHuman Tools -This document lists all available tools that AlphaHuman can use to interact with external services and perform actions. Tools are organized by integration and automatically updated when the app loads. +This document lists all available tools that AlphaHuman can use to interact with external services and perform actions. Tools are organized by integration and automatically updated during build time. ## Overview -AlphaHuman has access to **104 tools** across **3 integrations**. +AlphaHuman has access to **4 tools** across **3 integrations** organized into **6 categories**. **Quick Statistics:** +- **Telegram**: 2 tools +- **Notion**: 1 tools +- **Gmail**: 1 tools -- **Github**: 72 tools -- **Gmail**: 7 tools -- **Notion**: 25 tools +## Environment Configuration + +Tools are available in different environments with varying capabilities: + +### Development Environment + +Local development environment with full access + +- **Access Level**: Full access to all tools +- **Rate Limits**: Relaxed for testing +- **Authentication**: Development credentials +- **Logging**: Verbose logging enabled + +### Production Environment + +Production environment with security restrictions + +- **Access Level**: Production-safe tools only +- **Rate Limits**: Standard API limits enforced +- **Authentication**: Production credentials required +- **Logging**: Essential logs only + +### Testing Environment + +Testing environment for automated validation + +- **Access Level**: Safe tools for automated testing +- **Rate Limits**: Testing-specific limits +- **Authentication**: Test credentials +- **Logging**: Test execution logs + +## Tool Categories + +### Communication + +Tools for messaging, email, and social interaction + +- **Skills**: 2 +- **Tools**: 3 +- **Available Skills**: Telegram, Gmail + +### Productivity + +Tools for task management, note-taking, and organization + +- **Skills**: 1 +- **Tools**: 1 +- **Available Skills**: Notion ## Available Tools -### Github Tools - -This skill provides 72 tools for github integration. - -#### list-repos - -**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 -- **visibility** (string): Filter by visibility - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-repos", - "parameters": { - "limit": 10, - "owner": "example_owner", - "sort": "example_sort", - "visibility": "example_visibility" - } -} -``` - ---- - -#### get-repo - -**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" } } -``` - ---- - -#### create-repo - -**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 -- **visibility** (string): Repository visibility - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "create-repo", - "parameters": { - "auto_init": true, - "description": "example_description", - "name": "example_name", - "visibility": "example_visibility" - } -} -``` - ---- - -#### fork-repo - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "fork-repo", - "parameters": { - "fork_name": "example_fork_name", - "owner": "example_owner", - "repo": "example_repo" - } -} -``` - ---- - -#### delete-repo - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "delete-repo", - "parameters": { "confirm": true, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### clone-repo - -**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" } } -``` - ---- - -#### list-collaborators - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-collaborators", - "parameters": { "limit": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### add-collaborator - -**Description**: Add a collaborator to a repository - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **permission** (string): Permission level to grant -- **repo** (string) **(required)**: Repository name -- **username** (string) **(required)**: GitHub username of the collaborator to add - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "add-collaborator", - "parameters": { - "owner": "example_owner", - "permission": "example_permission", - "repo": "example_repo", - "username": "example_username" - } -} -``` - ---- - -#### remove-collaborator - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "remove-collaborator", - "parameters": { "owner": "example_owner", "repo": "example_repo", "username": "example_username" } -} -``` - ---- - -#### list-topics - -**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" } } -``` - ---- - -#### set-topics - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "set-topics", - "parameters": { "owner": "example_owner", "repo": "example_repo", "topics": [] } -} -``` - ---- - -#### list-languages - -**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" } } -``` - ---- - -#### list-issues - -**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 -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **state** (string): Filter by state - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-issues", - "parameters": { - "assignee": "example_assignee", - "label": "example_label", - "limit": 10, - "owner": "example_owner", - "repo": "example_repo", - "state": "example_state" - } -} -``` - ---- - -#### get-issue - -**Description**: Get detailed information about a specific issue - -**Parameters**: - -- **number** (number) **(required)**: Issue number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-issue", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### create-issue - -**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 -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **title** (string) **(required)**: Issue title - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "create-issue", - "parameters": { - "assignees": [], - "body": "example_body", - "labels": [], - "owner": "example_owner", - "repo": "example_repo", - "title": "example_title" - } -} -``` - ---- - -#### close-issue - -**Description**: Close an issue - -**Parameters**: - -- **number** (number) **(required)**: Issue number -- **owner** (string) **(required)**: Repository owner -- **reason** (string): Reason for closing -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "close-issue", - "parameters": { - "number": 10, - "owner": "example_owner", - "reason": "example_reason", - "repo": "example_repo" - } -} -``` - ---- - -#### reopen-issue - -**Description**: Reopen a closed issue - -**Parameters**: - -- **number** (number) **(required)**: Issue number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "reopen-issue", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### edit-issue - -**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 -- **repo** (string) **(required)**: Repository name -- **title** (string): New issue title - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "edit-issue", - "parameters": { - "body": "example_body", - "number": 10, - "owner": "example_owner", - "repo": "example_repo", - "title": "example_title" - } -} -``` - ---- - -#### comment-on-issue - -**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 -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "comment-on-issue", - "parameters": { - "body": "example_body", - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } -} -``` - ---- - -#### list-issue-comments - -**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 -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-issue-comments", - "parameters": { "limit": 10, "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### add-issue-labels - -**Description**: Add labels to an issue - -**Parameters**: - -- **labels** (array) **(required)**: Labels to add -- **number** (number) **(required)**: Issue number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "add-issue-labels", - "parameters": { "labels": [], "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### remove-issue-labels - -**Description**: Remove labels from an issue - -**Parameters**: - -- **labels** (array) **(required)**: Labels to remove -- **number** (number) **(required)**: Issue number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "remove-issue-labels", - "parameters": { "labels": [], "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### add-issue-assignees - -**Description**: Add assignees to an issue - -**Parameters**: - -- **assignees** (array) **(required)**: Usernames to assign -- **number** (number) **(required)**: Issue number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "add-issue-assignees", - "parameters": { "assignees": [], "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### remove-issue-assignees - -**Description**: Remove assignees from an issue - -**Parameters**: - -- **assignees** (array) **(required)**: Usernames to remove -- **number** (number) **(required)**: Issue number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "remove-issue-assignees", - "parameters": { "assignees": [], "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### list-prs - -**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 -- **repo** (string) **(required)**: Repository name -- **state** (string): Filter by state - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-prs", - "parameters": { - "base": "example_base", - "limit": 10, - "owner": "example_owner", - "repo": "example_repo", - "state": "example_state" - } -} -``` - ---- - -#### get-pr - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-pr", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### create-pr - -**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 -- **head** (string) **(required)**: The branch containing the changes (e.g. 'feature-branch' or 'user:feature-branch') -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **title** (string) **(required)**: Pull request title - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "create-pr", - "parameters": { - "base": "example_base", - "body": "example_body", - "draft": true, - "head": "example_head", - "owner": "example_owner", - "repo": "example_repo", - "title": "example_title" - } -} -``` - ---- - -#### close-pr - -**Description**: Close a pull request without merging - -**Parameters**: - -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "close-pr", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### reopen-pr - -**Description**: Reopen a closed pull request - -**Parameters**: - -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "reopen-pr", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### merge-pr - -**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 -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "merge-pr", - "parameters": { - "commit_message": "example_commit_message", - "delete_branch": true, - "method": "example_method", - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } -} -``` - ---- - -#### edit-pr - -**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 -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **title** (string): New pull request title - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "edit-pr", - "parameters": { - "base": "example_base", - "body": "example_body", - "number": 10, - "owner": "example_owner", - "repo": "example_repo", - "title": "example_title" - } -} -``` - ---- - -#### comment-on-pr - -**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 -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "comment-on-pr", - "parameters": { - "body": "example_body", - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } -} -``` - ---- - -#### list-pr-comments - -**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 -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-pr-comments", - "parameters": { "limit": 10, "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### list-pr-reviews - -**Description**: List reviews on a pull request - -**Parameters**: - -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-pr-reviews", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### create-pr-review - -**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 -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "create-pr-review", - "parameters": { - "body": "example_body", - "event": "example_event", - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } -} -``` - ---- - -#### list-pr-files - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-pr-files", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### get-pr-diff - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-pr-diff", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### get-pr-checks - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-pr-checks", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### request-pr-reviewers - -**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 -- **reviewers** (array) **(required)**: Usernames to request review from - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "request-pr-reviewers", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo", "reviewers": [] } -} -``` - ---- - -#### mark-pr-ready - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "mark-pr-ready", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### search-repos - -**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) -- **sort** (string): Sort field - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "search-repos", - "parameters": { - "limit": 10, - "order": "example_order", - "query": "example_query", - "sort": "example_sort" - } -} -``` - ---- - -#### search-issues - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "search-issues", - "parameters": { "limit": 10, "query": "example_query", "sort": "example_sort" } -} -``` - ---- - -#### search-code - -**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) -- **repo** (string): Restrict search to a specific repo (owner/name format) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "search-code", - "parameters": { - "language": "example_language", - "limit": 10, - "query": "example_query", - "repo": "example_repo" - } -} -``` - ---- - -#### search-commits - -**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) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "search-commits", - "parameters": { "limit": 10, "query": "example_query", "repo": "example_repo" } -} -``` - ---- - -#### view-file - -**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 -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "view-file", - "parameters": { - "owner": "example_owner", - "path": "example_path", - "ref": "example_ref", - "repo": "example_repo" - } -} -``` - ---- - -#### list-directory - -**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 -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-directory", - "parameters": { - "owner": "example_owner", - "path": "example_path", - "ref": "example_ref", - "repo": "example_repo" - } -} -``` - ---- - -#### get-readme - -**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" } } -``` - ---- - -#### list-releases - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-releases", - "parameters": { "limit": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### get-release - -**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') - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-release", - "parameters": { "owner": "example_owner", "repo": "example_repo", "tag": "example_tag" } -} -``` - ---- - -#### create-release - -**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) -- **owner** (string) **(required)**: Repository owner -- **prerelease** (boolean): Mark as a pre-release -- **repo** (string) **(required)**: Repository name -- **tag** (string) **(required)**: Tag name for the release (e.g. 'v1.0.0') -- **target** (string): Target commitish (branch or commit SHA) for the tag -- **title** (string): Release title - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "create-release", - "parameters": { - "draft": true, - "generate_notes": true, - "notes": "example_notes", - "owner": "example_owner", - "prerelease": true, - "repo": "example_repo", - "tag": "example_tag", - "target": "example_target", - "title": "example_title" - } -} -``` - ---- - -#### delete-release - -**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 -- **tag** (string) **(required)**: Release tag name to delete - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "delete-release", - "parameters": { - "cleanup_tag": true, - "owner": "example_owner", - "repo": "example_repo", - "tag": "example_tag" - } -} -``` - ---- - -#### list-release-assets - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-release-assets", - "parameters": { "owner": "example_owner", "repo": "example_repo", "tag": "example_tag" } -} -``` - ---- - -#### get-latest-release - -**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" } } -``` - ---- - -#### list-gists - -**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" } } -``` - ---- - -#### get-gist - -**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" } } -``` - ---- - -#### create-gist - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "create-gist", - "parameters": { "description": "example_description", "files": {}, "public": true } -} -``` - ---- - -#### edit-gist - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "edit-gist", - "parameters": { "description": "example_description", "files": {}, "gist_id": "example_gist_id" } -} -``` - ---- - -#### delete-gist - -**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" } } -``` - ---- - -#### clone-gist - -**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" } } -``` - ---- - -#### list-workflows - -**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" } } -``` - ---- - -#### list-workflow-runs - -**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 -- **repo** (string) **(required)**: Repository name -- **status** (string): Filter by status -- **workflow_id** (string): Filter by workflow ID or filename (e.g. 'ci.yml') - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-workflow-runs", - "parameters": { - "branch": "example_branch", - "limit": 10, - "owner": "example_owner", - "repo": "example_repo", - "status": "example_status", - "workflow_id": "example_workflow_id" - } -} -``` - ---- - -#### get-workflow-run - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-workflow-run", - "parameters": { "owner": "example_owner", "repo": "example_repo", "run_id": 10 } -} -``` - ---- - -#### list-run-jobs - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-run-jobs", - "parameters": { "owner": "example_owner", "repo": "example_repo", "run_id": 10 } -} -``` - ---- - -#### get-run-logs - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-run-logs", - "parameters": { "owner": "example_owner", "repo": "example_repo", "run_id": 10 } -} -``` - ---- - -#### rerun-workflow - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "rerun-workflow", - "parameters": { "owner": "example_owner", "repo": "example_repo", "run_id": 10 } -} -``` - ---- - -#### cancel-workflow-run - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "cancel-workflow-run", - "parameters": { "owner": "example_owner", "repo": "example_repo", "run_id": 10 } -} -``` - ---- - -#### trigger-workflow - -**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 -- **repo** (string) **(required)**: Repository name -- **workflow_id** (string) **(required)**: Workflow ID or filename (e.g. 'deploy.yml') - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "trigger-workflow", - "parameters": { - "inputs": {}, - "owner": "example_owner", - "ref": "example_ref", - "repo": "example_repo", - "workflow_id": "example_workflow_id" - } -} -``` - ---- - -#### view-workflow-yaml - -**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') - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "view-workflow-yaml", - "parameters": { - "owner": "example_owner", - "repo": "example_repo", - "workflow_id": "example_workflow_id" - } -} -``` - ---- - -#### list-notifications - -**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 } } -``` - ---- - -#### mark-notification-read - -**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" } } -``` - ---- - -#### mark-all-notifications-read - -**Description**: Mark all notifications as read - -**Parameters**: _None_ - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "mark-all-notifications-read", "parameters": {} } -``` - ---- - -#### gh-api - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "gh-api", - "parameters": { "body": {}, "endpoint": "example_endpoint", "method": "example_method" } -} -``` - ---- - ### Gmail Tools -This skill provides 7 tools for gmail integration. +**Category**: Communication -#### get-email +This skill provides 1 tool for gmail integration. -**Description**: Get full details of a specific email by its ID, including headers, body content, and attachments. +#### send_email + +**Description**: Send an email via Gmail **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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-email", - "parameters": { - "format": "example_format", - "include_body": true, - "message_id": "example_message_id" - } -} -``` - ---- - -#### get-emails - -**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"]) -- **maxResults** (number): Alias for max_results (e.g. for frontend calls) -- **max_results** (number): Maximum number of emails to return (default: 20, max: 100) -- **page_token** (string): Token for pagination (returned from previous request) -- **query** (string): Search query using Gmail search syntax (e.g., "from:example@gmail.com", "subject:meeting", "is:unread") - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-emails", - "parameters": { - "accessToken": "example_accessToken", - "include_spam_trash": true, - "label_ids": [], - "maxResults": 10, - "max_results": 10, - "page_token": "example_page_token", - "query": "example_query" - } -} -``` - ---- - -#### get-labels - -**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" } } -``` - ---- - -#### get-profile - -**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" } } -``` - ---- - -#### mark-email - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "mark-email", - "parameters": { "action": "example_action", "label_ids": [], "message_ids": [] } -} -``` - ---- - -#### search-emails - -**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) -- **query** (string) **(required)**: Gmail search query (e.g., "from:john@example.com subject:meeting is:unread", "has:attachment after:2023/01/01") - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "search-emails", - "parameters": { - "include_spam_trash": true, - "max_results": 10, - "page_token": "example_page_token", - "query": "example_query" - } -} -``` - ---- - -#### send-email - -**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) -- **body_text** (string): Plain text email body (optional if body_html provided) -- **cc** (array): CC recipients (optional) -- **reply_to_message_id** (string): Message ID being replied to (optional) +- **to** (string) **(required)**: Recipient email address - **subject** (string) **(required)**: Email subject line -- **thread_id** (string): Thread ID for replies (optional) -- **to** (array) **(required)**: Primary recipients +- **body** (string) **(required)**: Email body content +- **attachments** (array): File attachments **Usage Context**: Available in all environments **Example**: - ```json { - "tool": "send-email", + "tool": "send_email", "parameters": { - "attachments": [], - "bcc": [], - "body_html": "example_body_html", - "body_text": "example_body_text", - "cc": [], - "reply_to_message_id": "example_reply_to_message_id", + "to": "example_to", "subject": "example_subject", - "thread_id": "example_thread_id", - "to": [] + "body": "example_body" } } ``` @@ -1998,534 +96,86 @@ This skill provides 7 tools for gmail integration. ### Notion Tools -This skill provides 25 tools for notion integration. +**Category**: Productivity -#### append-blocks +This skill provides 1 tool for notion integration. -**Description**: Append child blocks to a page or block. Supports various block types. +#### create_page + +**Description**: Create a new page in Notion workspace **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" } -} -``` - ---- - -#### append-text - -**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" } } -``` - ---- - -#### create-comment - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "create-comment", - "parameters": { - "discussion_id": "example_discussion_id", - "page_id": "example_page_id", - "text": "example_text" - } -} -``` - ---- - -#### create-database - -**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 - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "create-database", - "parameters": { - "parent_page_id": "example_parent_page_id", - "properties": "example_properties", - "title": "example_title" - } -} -``` - ---- - -#### create-page - -**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) -- **properties** (string): JSON string of additional properties (for database pages) +- **parent_id** (string) **(required)**: Parent database or page ID - **title** (string) **(required)**: Page title +- **content** (array): Page content blocks +- **properties** (object): Page properties for database pages **Usage Context**: Available in all environments **Example**: - ```json { - "tool": "create-page", + "tool": "create_page", "parameters": { - "content": "example_content", "parent_id": "example_parent_id", - "parent_type": "example_parent_type", - "properties": "example_properties", - "title": "example_title" + "title": "example_title", + "content": [] } } ``` --- -#### delete-block +### Telegram Tools -**Description**: Delete a block. This permanently removes the block from Notion. +**Category**: Communication + +This skill provides 2 tools for telegram integration. + +#### send_message + +**Description**: Send a message to a Telegram chat or user **Parameters**: - -- **block_id** (string) **(required)**: The block ID to delete +- **chat_id** (string) **(required)**: Telegram chat ID or username +- **message** (string) **(required)**: Message text to send +- **parse_mode** (string): Message formatting mode Options: `HTML`, `Markdown` **Usage Context**: Available in all environments **Example**: - -```json -{ "tool": "delete-block", "parameters": { "block_id": "example_block_id" } } -``` - ---- - -#### delete-page - -**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" } } -``` - ---- - -#### get-block - -**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" } } -``` - ---- - -#### get-block-children - -**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 } } -``` - ---- - -#### get-database - -**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" } } -``` - ---- - -#### get-page - -**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" } } -``` - ---- - -#### get-page-content - -**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) - -**Usage Context**: Available in all environments - -**Example**: - ```json { - "tool": "get-page-content", - "parameters": { "page_id": "example_page_id", "page_size": 10, "recursive": "example_recursive" } -} -``` - ---- - -#### get-user - -**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" } } -``` - ---- - -#### list-all-databases - -**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 } } -``` - ---- - -#### list-all-pages - -**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 } } -``` - ---- - -#### list-comments - -**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 } } -``` - ---- - -#### list-users - -**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 } } -``` - ---- - -#### query-database - -**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) -- **sorts** (string): JSON string of sorts array (Notion sort syntax) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "query-database", + "tool": "send_message", "parameters": { - "database_id": "example_database_id", - "filter": "example_filter", - "page_size": 10, - "sorts": "example_sorts" + "chat_id": "example_chat_id", + "message": "example_message", + "parse_mode": "HTML" } } ``` --- -#### search +#### get_chat_history -**Description**: Search for pages and databases in your Notion workspace. Can filter by type (page or database) and returns matching results. +**Description**: Retrieve message history from a Telegram chat **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) +- **chat_id** (string) **(required)**: Telegram chat ID or username +- **limit** (number): Number of messages to retrieve (max 100) +- **offset** (number): Offset for pagination **Usage Context**: Available in all environments **Example**: - ```json { - "tool": "search", - "parameters": { "filter": "example_filter", "page_size": 10, "query": "example_query" } -} -``` - ---- - -#### summarize-pages - -**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_ - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "summarize-pages", "parameters": {} } -``` - ---- - -#### sync-now - -**Description**: Trigger an immediate Notion sync to refresh local data. Returns sync results including counts of synced pages and databases. - -**Parameters**: _None_ - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "sync-now", "parameters": {} } -``` - ---- - -#### sync-status - -**Description**: Get the current Notion sync status including last sync time, total synced pages/databases, sync progress, and any errors. - -**Parameters**: _None_ - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "sync-status", "parameters": {} } -``` - ---- - -#### update-block - -**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"}}]}} - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "update-block", + "tool": "get_chat_history", "parameters": { - "archived": "example_archived", - "block_id": "example_block_id", - "content": "example_content" - } -} -``` - ---- - -#### update-database - -**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) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "update-database", - "parameters": { - "database_id": "example_database_id", - "properties": "example_properties", - "title": "example_title" - } -} -``` - ---- - -#### update-page - -**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 -- **title** (string): New title (optional) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "update-page", - "parameters": { - "archived": "example_archived", - "page_id": "example_page_id", - "properties": "example_properties", - "title": "example_title" + "chat_id": "example_chat_id", + "limit": 10, + "offset": 10 } } ``` @@ -2535,28 +185,99 @@ 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 +- Test credentials are available for development and testing environments ### Rate Limiting - - Tools automatically respect API rate limits of external services - Intelligent retry logic handles temporary failures with exponential backoff +- Bulk operations are automatically chunked to avoid hitting limits +- Rate limit status is monitored and reported in real-time ### Error Handling - - All tools return structured error responses with detailed information - Network failures trigger automatic retry with configurable attempts +- Invalid parameters return clear validation messages with examples +- Tool execution timeouts are handled gracefully with partial results + +### Security & Privacy +- Input validation is performed on all parameters using JSON Schema +- Output sanitization prevents injection attacks and data leakage +- Sensitive data is never logged or exposed in error messages +- All API communications use secure protocols (HTTPS/TLS) + +### Performance Optimization +- Tool results are cached when appropriate to reduce API calls +- Parallel execution is used for independent operations +- Connection pooling minimizes overhead for repeated API calls +- Background sync keeps data fresh without blocking operations + +### Monitoring & Observability +- Tool execution metrics are collected for performance analysis +- Error rates and response times are monitored continuously +- Debug logging is available in development environments +- Tool usage analytics help optimize integration performance + +## Skill Management + +Tools are provided by Skills, which are JavaScript modules running in a secure V8 runtime: + +- **Discovery**: Tools are automatically discovered at build time from running skills +- **Lifecycle**: Skills can be enabled/disabled independently without affecting others +- **Configuration**: Each skill has its own configuration panel with setup wizards +- **Updates**: Skills are updated through Git submodules and the Skills management system +- **Security**: Skills run in sandboxed environments with limited system access + +## Integration Architecture + +### V8 Runtime +- Skills execute in isolated V8 JavaScript contexts on desktop platforms +- Mobile platforms use lightweight alternatives with server-side execution +- Memory limits and execution timeouts prevent resource exhaustion +- Inter-skill communication is managed through secure message passing + +### API Bridge +- Tools communicate with external services through standardized API bridges +- Rate limiting, retry logic, and error handling are implemented at the bridge level +- Authentication tokens are managed centrally and shared across tools +- Response caching and optimization are handled transparently + +### Data Flow +1. Tool request received from AI agent +2. Input validation and parameter processing +3. Skill execution in secure V8 context +4. API calls through standardized bridges +5. Response processing and formatting +6. Result delivery to AI agent + +## Support & Troubleshooting + +### Common Issues +1. **Tool Not Available**: Check if the associated skill is enabled in Settings → Skills +2. **Authentication Errors**: Verify credentials in the skill's configuration panel +3. **Rate Limit Exceeded**: Wait for the limit to reset or upgrade your API plan +4. **Invalid Parameters**: Review the parameter documentation and examples above + +### Getting Help +- **Skill Documentation**: Each skill has detailed setup and usage instructions +- **Debug Logs**: Enable verbose logging in development mode for detailed error information +- **Community Support**: Join our Discord community for help from other users +- **Technical Support**: Contact our support team for critical issues + +### Contributing +- **New Tools**: Submit tool requests through our GitHub repository +- **Bug Reports**: Report issues with specific tools and include error logs +- **Improvements**: Suggest enhancements to existing tools and their documentation --- **Tool Statistics** - -- Total Tools: 104 +- Total Tools: 4 - Active Skills: 3 -- Last Updated: 2026-03-06T10:39:36.767Z +- Categories: 6 +- Last Updated: 2026-03-11T12:05:59.019Z -_This file was automatically generated when the app loaded._ -_Tools are discovered from the running V8 skills runtime._ +*This file was automatically generated at build time from the V8 skills runtime.* +*For the most up-to-date information, regenerate this file by running `yarn tools:generate`.* diff --git a/src/lib/tools/auto-update.ts b/src/lib/tools/auto-update.ts index 804a494aa..32f1130d6 100644 --- a/src/lib/tools/auto-update.ts +++ b/src/lib/tools/auto-update.ts @@ -6,7 +6,6 @@ */ import { invoke } from '@tauri-apps/api/core'; -import { clearToolsCache } from '../ai/tools/loader'; import { forceToolsCacheRefresh } from './file-watcher'; // Prevent excessive updates - limit to once per 10 seconds diff --git a/src/lib/tools/file-watcher.ts b/src/lib/tools/file-watcher.ts index 76c2f6dc9..8dbb84517 100644 --- a/src/lib/tools/file-watcher.ts +++ b/src/lib/tools/file-watcher.ts @@ -99,10 +99,10 @@ function simpleHash(str: string): number { /** * Force a cache refresh (useful for manual triggers) */ -export function forceToolsCacheRefresh(): Promise { +export async function forceToolsCacheRefresh(): Promise { console.log('🔄 Forcing tools cache refresh...'); clearToolsCache(); clearAICache(); lastModifiedTime = null; // Reset to trigger next check - return loadAIConfig(); + await loadAIConfig(); } diff --git a/src/services/__tests__/agentLoop.test.ts b/src/services/__tests__/agentLoop.test.ts deleted file mode 100644 index 0e985e246..000000000 --- a/src/services/__tests__/agentLoop.test.ts +++ /dev/null @@ -1,536 +0,0 @@ -import { beforeEach, describe, expect, type Mock, test, vi } from 'vitest'; - -import type { - AgentExecutionOptions, - AgentExecutionResult, - AgentToolExecution, - AgentToolSchema, -} from '../../types/agent'; -import { AgentLoopService } from '../agentLoop'; -import { AgentToolRegistry } from '../agentToolRegistry'; -import { apiClient } from '../apiClient'; - -// Mock dependencies -vi.mock('../agentToolRegistry'); -vi.mock('../apiClient'); - -describe('AgentLoopService', () => { - let service: AgentLoopService; - const mockToolRegistry = AgentToolRegistry as vi.MockedClass; - const mockApiClient = apiClient as { post: Mock }; - - const mockToolSchemas: AgentToolSchema[] = [ - { - type: 'function', - function: { - name: 'github_list_issues', - description: 'List GitHub issues for a repository', - parameters: { - type: 'object', - properties: { - owner: { type: 'string', description: 'Repository owner' }, - repo: { type: 'string', description: 'Repository name' }, - }, - required: ['owner', 'repo'], - }, - }, - }, - { - type: 'function', - function: { - name: 'notion_create_page', - description: 'Create a new Notion page', - parameters: { - type: 'object', - properties: { - title: { type: 'string', description: 'Page title' }, - content: { type: 'string', description: 'Page content' }, - }, - required: ['title'], - }, - }, - }, - ]; - - beforeEach(() => { - service = AgentLoopService.getInstance(); - vi.clearAllMocks(); - - // Setup default mock implementations - const mockRegistryInstance = { loadToolSchemas: vi.fn(), executeTool: vi.fn() }; - - mockToolRegistry.getInstance.mockReturnValue(mockRegistryInstance as any); - mockRegistryInstance.loadToolSchemas.mockResolvedValue(mockToolSchemas); - }); - - describe('executeTask', () => { - test('should execute simple task without tool calls', async () => { - const mockResponse = { - choices: [ - { - message: { - role: 'assistant' as const, - content: 'Hello! How can I help you today?', - tool_calls: undefined, - }, - finish_reason: 'stop' as const, - }, - ], - usage: { prompt_tokens: 20, completion_tokens: 10, total_tokens: 30 }, - }; - - mockApiClient.post.mockResolvedValue({ data: mockResponse }); - - const result = await service.executeTask('Hello', 'conv_123', { - maxIterations: 5, - timeoutMs: 30000, - }); - - expect(result.status).toBe('completed'); - expect(result.finalResponse).toBe('Hello! How can I help you today?'); - expect(result.iterations).toBe(1); - expect(result.toolExecutions).toHaveLength(0); - expect(result.executionTime).toBeGreaterThan(0); - - // Verify API call format - expect(mockApiClient.post).toHaveBeenCalledWith( - '/api/v1/conversations/conv_123/messages', - expect.objectContaining({ - model: expect.any(String), - messages: expect.arrayContaining([ - expect.objectContaining({ role: 'user', content: 'Hello' }), - ]), - tools: mockToolSchemas, - tool_choice: 'auto', - }) - ); - }); - - test('should execute task with single tool call', async () => { - const mockToolCallResponse = { - choices: [ - { - message: { - role: 'assistant' as const, - content: null, - tool_calls: [ - { - id: 'call_123', - type: 'function' as const, - function: { - name: 'github_list_issues', - arguments: '{"owner":"user","repo":"test"}', - }, - }, - ], - }, - finish_reason: 'tool_calls' as const, - }, - ], - }; - - const mockFinalResponse = { - choices: [ - { - message: { - role: 'assistant' as const, - content: 'I found 3 open issues in your repository.', - tool_calls: undefined, - }, - finish_reason: 'stop' as const, - }, - ], - usage: { prompt_tokens: 50, completion_tokens: 20, total_tokens: 70 }, - }; - - const mockToolExecution: AgentToolExecution = { - id: 'exec_123', - toolName: 'list_issues', - skillId: 'github', - arguments: '{"owner":"user","repo":"test"}', - status: 'success', - startTime: Date.now() - 1500, - endTime: Date.now(), - executionTimeMs: 1500, - result: '{"issues":[{"title":"Bug fix","number":1}]}', - }; - - // Setup mocks - const mockRegistryInstance = mockToolRegistry.getInstance(); - mockRegistryInstance.executeTool.mockResolvedValue(mockToolExecution); - - mockApiClient.post - .mockResolvedValueOnce({ data: mockToolCallResponse }) - .mockResolvedValueOnce({ data: mockFinalResponse }); - - const result = await service.executeTask('Show me GitHub issues', 'conv_123'); - - expect(result.status).toBe('completed'); - expect(result.finalResponse).toBe('I found 3 open issues in your repository.'); - expect(result.iterations).toBe(2); - expect(result.toolExecutions).toHaveLength(1); - expect(result.toolExecutions[0].toolName).toBe('list_issues'); - expect(result.toolExecutions[0].status).toBe('success'); - - // Verify tool execution was called with correct parameters - expect(mockRegistryInstance.executeTool).toHaveBeenCalledWith( - 'github', - 'list_issues', - '{"owner":"user","repo":"test"}' - ); - }); - - test('should handle multiple tool calls in sequence', async () => { - const mockFirstToolCallResponse = { - choices: [ - { - message: { - role: 'assistant' as const, - content: null, - tool_calls: [ - { - id: 'call_1', - type: 'function' as const, - function: { - name: 'github_list_issues', - arguments: '{"owner":"user","repo":"test"}', - }, - }, - ], - }, - finish_reason: 'tool_calls' as const, - }, - ], - }; - - const mockSecondToolCallResponse = { - choices: [ - { - message: { - role: 'assistant' as const, - content: null, - tool_calls: [ - { - id: 'call_2', - type: 'function' as const, - function: { name: 'notion_create_page', arguments: '{"title":"Issues Summary"}' }, - }, - ], - }, - finish_reason: 'tool_calls' as const, - }, - ], - }; - - const mockFinalResponse = { - choices: [ - { - message: { - role: 'assistant' as const, - content: 'I created a summary page with your GitHub issues.', - tool_calls: undefined, - }, - finish_reason: 'stop' as const, - }, - ], - }; - - const mockToolExecution1: AgentToolExecution = { - id: 'exec_1', - toolName: 'list_issues', - skillId: 'github', - arguments: '{"owner":"user","repo":"test"}', - status: 'success', - startTime: Date.now() - 2000, - endTime: Date.now() - 1000, - executionTimeMs: 1000, - result: '{"issues":[{"title":"Bug fix","number":1}]}', - }; - - const mockToolExecution2: AgentToolExecution = { - id: 'exec_2', - toolName: 'create_page', - skillId: 'notion', - arguments: '{"title":"Issues Summary"}', - status: 'success', - startTime: Date.now() - 800, - endTime: Date.now(), - executionTimeMs: 800, - result: '{"page_id":"page_123"}', - }; - - // Setup mocks - const mockRegistryInstance = mockToolRegistry.getInstance(); - mockRegistryInstance.executeTool - .mockResolvedValueOnce(mockToolExecution1) - .mockResolvedValueOnce(mockToolExecution2); - - mockApiClient.post - .mockResolvedValueOnce({ data: mockFirstToolCallResponse }) - .mockResolvedValueOnce({ data: mockSecondToolCallResponse }) - .mockResolvedValueOnce({ data: mockFinalResponse }); - - const result = await service.executeTask( - 'Get GitHub issues and create a summary page', - 'conv_123', - { maxIterations: 5 } - ); - - expect(result.status).toBe('completed'); - expect(result.iterations).toBe(3); - expect(result.toolExecutions).toHaveLength(2); - expect(result.toolExecutions[0].skillId).toBe('github'); - expect(result.toolExecutions[1].skillId).toBe('notion'); - }); - - test('should handle tool execution timeout', async () => { - const result = await service.executeTask( - 'Test timeout', - 'conv_123', - { maxIterations: 1, timeoutMs: 100 } // Very short timeout - ); - - // The timeout logic depends on how it's implemented in the actual service - // This test may need adjustment based on the actual implementation - expect(result.status).toBe('timeout'); - expect(result.error).toContain('timeout'); - }); - - test('should respect maximum iterations limit', async () => { - const mockToolCallResponse = { - choices: [ - { - message: { - role: 'assistant' as const, - tool_calls: [ - { - id: 'call_1', - type: 'function' as const, - function: { name: 'github_list_issues', arguments: '{}' }, - }, - ], - }, - finish_reason: 'tool_calls' as const, - }, - ], - }; - - // Mock to always return tool calls (infinite loop scenario) - mockApiClient.post.mockResolvedValue({ data: mockToolCallResponse }); - - const mockToolExecution: AgentToolExecution = { - id: 'exec_1', - toolName: 'list_issues', - skillId: 'github', - arguments: '{}', - status: 'success', - startTime: Date.now() - 100, - endTime: Date.now(), - executionTimeMs: 100, - result: '{}', - }; - - const mockRegistryInstance = mockToolRegistry.getInstance(); - mockRegistryInstance.executeTool.mockResolvedValue(mockToolExecution); - - const result = await service.executeTask('Infinite loop test', 'conv_123', { - maxIterations: 2, - timeoutMs: 10000, - }); - - expect(result.status).toBe('max_iterations'); - expect(result.iterations).toBe(2); - expect(result.error).toContain('maximum iterations'); - }); - - test('should handle tool execution error gracefully', async () => { - const mockToolCallResponse = { - choices: [ - { - message: { - role: 'assistant' as const, - tool_calls: [ - { - id: 'call_1', - type: 'function' as const, - function: { name: 'invalid_tool', arguments: '{}' }, - }, - ], - }, - finish_reason: 'tool_calls' as const, - }, - ], - }; - - const mockErrorResponse = { - choices: [ - { - message: { - role: 'assistant' as const, - content: 'I encountered an error while executing the tool.', - tool_calls: undefined, - }, - finish_reason: 'stop' as const, - }, - ], - }; - - const mockToolExecution: AgentToolExecution = { - id: 'exec_1', - toolName: 'invalid_tool', - skillId: 'unknown', - arguments: '{}', - status: 'error', - startTime: Date.now() - 100, - endTime: Date.now(), - executionTimeMs: 100, - errorMessage: 'Tool not found', - }; - - const mockRegistryInstance = mockToolRegistry.getInstance(); - mockRegistryInstance.executeTool.mockResolvedValue(mockToolExecution); - - mockApiClient.post - .mockResolvedValueOnce({ data: mockToolCallResponse }) - .mockResolvedValueOnce({ data: mockErrorResponse }); - - const result = await service.executeTask('Test error handling', 'conv_123'); - - expect(result.status).toBe('completed'); - expect(result.toolExecutions).toHaveLength(1); - expect(result.toolExecutions[0].status).toBe('error'); - expect(result.toolExecutions[0].errorMessage).toBe('Tool not found'); - }); - - test('should handle API client errors', async () => { - mockApiClient.post.mockRejectedValue(new Error('Network error')); - - const result = await service.executeTask('Test API error', 'conv_123'); - - expect(result.status).toBe('error'); - expect(result.error).toContain('Network error'); - expect(result.iterations).toBe(0); - expect(result.toolExecutions).toHaveLength(0); - }); - - test('should parse tool name from function name correctly', async () => { - const mockToolCallResponse = { - choices: [ - { - message: { - role: 'assistant' as const, - tool_calls: [ - { - id: 'call_1', - type: 'function' as const, - function: { - name: 'github_list_issues', // Should parse to skillId=github, toolName=list_issues - arguments: '{"owner":"user","repo":"test"}', - }, - }, - ], - }, - finish_reason: 'tool_calls' as const, - }, - ], - }; - - const mockFinalResponse = { - choices: [ - { - message: { role: 'assistant' as const, content: 'Done', tool_calls: undefined }, - finish_reason: 'stop' as const, - }, - ], - }; - - const mockToolExecution: AgentToolExecution = { - id: 'exec_1', - toolName: 'list_issues', - skillId: 'github', - arguments: '{"owner":"user","repo":"test"}', - status: 'success', - startTime: Date.now() - 100, - endTime: Date.now(), - executionTimeMs: 100, - result: '{}', - }; - - const mockRegistryInstance = mockToolRegistry.getInstance(); - mockRegistryInstance.executeTool.mockResolvedValue(mockToolExecution); - - mockApiClient.post - .mockResolvedValueOnce({ data: mockToolCallResponse }) - .mockResolvedValueOnce({ data: mockFinalResponse }); - - await service.executeTask('Test tool parsing', 'conv_123'); - - // Verify correct parsing of skill ID and tool name - expect(mockRegistryInstance.executeTool).toHaveBeenCalledWith( - 'github', - 'list_issues', - '{"owner":"user","repo":"test"}' - ); - }); - }); - - describe('singleton behavior', () => { - test('should return the same instance', () => { - const instance1 = AgentLoopService.getInstance(); - const instance2 = AgentLoopService.getInstance(); - - expect(instance1).toBe(instance2); - }); - }); - - describe('task execution options', () => { - test('should use default options when none provided', async () => { - const mockResponse = { - choices: [ - { - message: { role: 'assistant' as const, content: 'Test response' }, - finish_reason: 'stop' as const, - }, - ], - }; - - mockApiClient.post.mockResolvedValue({ data: mockResponse }); - - const result = await service.executeTask('Test', 'conv_123'); - - // Should complete successfully with defaults - expect(result.status).toBe('completed'); - expect(result.executionTime).toBeGreaterThan(0); - }); - - test('should respect custom execution options', async () => { - const mockResponse = { - choices: [ - { - message: { role: 'assistant' as const, content: 'Test response' }, - finish_reason: 'stop' as const, - }, - ], - }; - - mockApiClient.post.mockResolvedValue({ data: mockResponse }); - - const customOptions: AgentExecutionOptions = { - maxIterations: 3, - timeoutMs: 5000, - model: 'gpt-3.5-turbo', - temperature: 0.7, - }; - - const result = await service.executeTask('Test', 'conv_123', customOptions); - - expect(result.status).toBe('completed'); - - // Verify custom options were passed to API - expect(mockApiClient.post).toHaveBeenCalledWith( - '/api/v1/conversations/conv_123/messages', - expect.objectContaining({ model: 'gpt-3.5-turbo', temperature: 0.7 }) - ); - }); - }); -}); diff --git a/src/store/threadSlice.ts b/src/store/threadSlice.ts index 8c4ccad25..72ce0f213 100644 --- a/src/store/threadSlice.ts +++ b/src/store/threadSlice.ts @@ -4,7 +4,6 @@ import { injectAll } from '../lib/ai/injector'; import type { Message } from '../lib/ai/providers/interface'; import { threadApi } from '../services/api/threadApi'; import type { Thread, ThreadMessage } from '../types/thread'; -import type { RootState } from './index'; interface ThreadState { // Existing local data (will be persisted) @@ -237,8 +236,13 @@ const threadSlice = createSlice({ : lastUserMessage; persistedMessages.push(stableMessage); // Keep state.messages in sync with the stable id - const idx = state.messages.findLastIndex(m => m.id === lastUserMessage.id); - if (idx !== -1) state.messages[idx] = stableMessage; + const messages = state.messages; + for (let i = messages.length - 1; i >= 0; i--) { + if (messages[i].id === lastUserMessage.id) { + messages[i] = stableMessage; + break; + } + } } }