1537 lines
60 KiB
TypeScript
1537 lines
60 KiB
TypeScript
import type { ConnectorTool } from "@rakazo/adapter-kit";
|
||
import {
|
||
AnalyzeVideoInputSchema,
|
||
BotSecretDestination,
|
||
BotSecretName,
|
||
EditImageInputSchema,
|
||
EditVideoInputSchema,
|
||
ExportVideoInputSchema,
|
||
GenerateImageInputSchema,
|
||
GenerateVideoInputSchema,
|
||
MEDIA_PLATFORM_PROFILES,
|
||
MODEL_TIERS,
|
||
SecretAskPurpose,
|
||
SecretHttpRequest,
|
||
TranscribeInputSchema,
|
||
USER_FACT_CATEGORIES,
|
||
} from "@rakazo/contracts";
|
||
import { ooLookFormat } from "@rakazo/core";
|
||
import { z } from "zod";
|
||
import { coachTools } from "./coach-tools.js";
|
||
import { mediaToolInputSchema } from "./media-tools.js";
|
||
import { NOTIFY_USER_BODY_MAX_LENGTH, NOTIFY_USER_TITLE_MAX_LENGTH } from "./notify-user-tool.js";
|
||
|
||
/** A media tool's parameters from its operation's input schema, plus `continue_after`. */
|
||
function mediaParameters(schema: z.ZodType, options?: { continueAfter?: boolean }) {
|
||
const json = z.toJSONSchema(schema, { io: "input" });
|
||
return mediaToolInputSchema(
|
||
{
|
||
properties: (json.properties ?? {}) as Record<string, unknown>,
|
||
required: json.required,
|
||
},
|
||
options,
|
||
);
|
||
}
|
||
|
||
export const SUBAGENT_MODEL_TIER_DESCRIPTION =
|
||
"Optional model tier: fast for quick simple work, code for programming, standard otherwise. Uses the model this space maps to the tier, or your own model when it maps none. Ignored when model_provider and model_id are set.";
|
||
|
||
const VIDEO_PRESETS_AND_FITS =
|
||
"Presets: vertical 9:16, landscape 16:9, square 1:1, portrait 4:5, classic 4:3, cinema 21:9. fit: crop (default) fills the frame and cuts the edges, pad adds bars, blur fills the bars with a blurred copy.";
|
||
|
||
/** "tiktok 600 s, …": each platform id with the longest video it takes. */
|
||
const VIDEO_PLATFORMS = Object.values(MEDIA_PLATFORM_PROFILES)
|
||
.map((profile) => `${profile.id} ${profile.duration.hardMaxMs / 1000} s`)
|
||
.join(", ");
|
||
|
||
export const DELEGATION_TOOL_NAMES = new Set([
|
||
"run_subagent",
|
||
"spawn_bot",
|
||
"create_project",
|
||
"archive_bot",
|
||
"delete_bot",
|
||
"handoff_to_bot",
|
||
"message_bot",
|
||
"delegate_task",
|
||
]);
|
||
|
||
export const builtinAgentTools: ConnectorTool[] = [
|
||
{
|
||
name: "computer_observe",
|
||
description:
|
||
"Capture the current screen of this bot's computer. Returns frame metadata and an image. Observe before coordinate-based actions and whenever another actor may have changed the desktop.",
|
||
inputSchema: { type: "object", properties: {} },
|
||
},
|
||
{
|
||
name: "computer_act",
|
||
description:
|
||
"Perform up to 24 ordered desktop actions on this bot's computer and return the resulting screen. Batch only predictable actions; stop before an outcome you need to inspect. Action kinds: click, move, down, up, type, key, scroll, wait.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
actions: {
|
||
type: "array",
|
||
items: {
|
||
type: "object",
|
||
properties: {
|
||
kind: {
|
||
type: "string",
|
||
enum: ["click", "move", "down", "up", "type", "key", "scroll", "wait"],
|
||
},
|
||
x: { type: "number" },
|
||
y: { type: "number" },
|
||
button: { type: "string", enum: ["left", "right"] },
|
||
double: { type: "boolean" },
|
||
text: { type: "string" },
|
||
key: { type: "string" },
|
||
modifiers: { type: "array", items: { type: "string" } },
|
||
direction: { type: "string", enum: ["up", "down"] },
|
||
amount: { type: "number" },
|
||
ms: { type: "number" },
|
||
},
|
||
required: ["kind"],
|
||
},
|
||
},
|
||
observe: { type: "boolean" },
|
||
settle_ms: { type: "number" },
|
||
},
|
||
required: ["actions"],
|
||
},
|
||
},
|
||
|
||
{
|
||
name: "browser_navigate",
|
||
description:
|
||
'Open a URL in the page browser on this bot\'s computer and return the document title. Prefer this over pixel clicks for web pages. If the result includes fallback:"computer_act", use computer_act on the desktop browser instead.',
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
url: { type: "string", description: "http(s) URL to open." },
|
||
},
|
||
required: ["url"],
|
||
},
|
||
},
|
||
{
|
||
name: "browser_snapshot",
|
||
description:
|
||
"Capture an accessibility-style snapshot of the current page with element refs (e1, e2, …). Use refs with browser_act. Prefer this over computer_observe for web pages. If fallback is computer_act, use the desktop tools instead.",
|
||
inputSchema: { type: "object", properties: {} },
|
||
readOnly: true,
|
||
},
|
||
{
|
||
name: "browser_act",
|
||
description:
|
||
'Interact with the current page in the browser (kinds: click, hover, fill, type, select, press, scroll, wait). Prefer this over computer_act for web pages so the user can watch live. If the result includes fallback:"computer_act", use computer_act instead.',
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
actions: {
|
||
type: "array",
|
||
items: {
|
||
type: "object",
|
||
properties: {
|
||
kind: {
|
||
type: "string",
|
||
enum: ["click", "hover", "fill", "type", "select", "press", "scroll", "wait"],
|
||
},
|
||
ref: {
|
||
type: "string",
|
||
description:
|
||
"Element ref from browser_snapshot (required for click, hover, fill, type, select; optional for press and scroll).",
|
||
},
|
||
text: { type: "string", description: "Text or option for fill, type, or select." },
|
||
key: {
|
||
type: "string",
|
||
description: "Key name for press (e.g. Enter, Escape, Tab, ArrowDown).",
|
||
},
|
||
direction: {
|
||
type: "string",
|
||
enum: ["up", "down"],
|
||
description: "Scroll direction for scroll (default down).",
|
||
},
|
||
amount: {
|
||
type: "number",
|
||
description: "Scroll step amount between 1 and 10 (default 3).",
|
||
},
|
||
ms: {
|
||
type: "number",
|
||
description: "Pause duration in milliseconds for wait (0 to 5000).",
|
||
},
|
||
},
|
||
required: ["kind"],
|
||
},
|
||
},
|
||
},
|
||
required: ["actions"],
|
||
},
|
||
},
|
||
{
|
||
name: "list_files",
|
||
description:
|
||
"List files and directories in this bot's home. On a Team Computer, relative paths use the bot folder; use shared/... for shared work or bots/... to inspect the Team root.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: { path: { type: "string" } },
|
||
},
|
||
},
|
||
{
|
||
name: "read_file",
|
||
description:
|
||
"Read a UTF-8 text file from this bot's home. Pass start_line and end_line (1-indexed, inclusive) to read a specific line range of a large file, or query when looking for specific sections, facts, or numbers in a long document or attachment so the most relevant passages are extracted. On a Team Computer, relative paths use the bot folder and shared/... accesses shared work. Open visual or binary files with open_path instead.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
path: { type: "string" },
|
||
query: {
|
||
type: "string",
|
||
description:
|
||
"Optional focus query to extract the relevant sections and facts from a long document.",
|
||
},
|
||
start_line: {
|
||
type: "integer",
|
||
description: "Optional 1-indexed first line to read (inclusive).",
|
||
},
|
||
end_line: {
|
||
type: "integer",
|
||
description: "Optional 1-indexed last line to read (inclusive).",
|
||
},
|
||
},
|
||
required: ["path"],
|
||
},
|
||
},
|
||
{
|
||
name: "write_file",
|
||
description:
|
||
"Write or overwrite a UTF-8 file in this bot's home. To modify an existing file without rewriting all of it, prefer edit_file. On a Team Computer, relative paths use the bot folder; use shared/... only for work other bots should share.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
path: { type: "string" },
|
||
content: { type: "string" },
|
||
},
|
||
required: ["path", "content"],
|
||
},
|
||
},
|
||
{
|
||
name: "edit_file",
|
||
description:
|
||
"Replace an exact text snippet in an existing UTF-8 file in this bot's home without rewriting the whole file. Prefer this over write_file when modifying existing files.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
path: { type: "string" },
|
||
old_text: {
|
||
type: "string",
|
||
description: "Exact existing text to find in the file.",
|
||
},
|
||
new_text: {
|
||
type: "string",
|
||
description: "Replacement text.",
|
||
},
|
||
replace_all: {
|
||
type: "boolean",
|
||
description: "Replace all occurrences of old_text instead of requiring a single match.",
|
||
},
|
||
},
|
||
required: ["path", "old_text", "new_text"],
|
||
},
|
||
},
|
||
{
|
||
name: "attach_file",
|
||
description:
|
||
"Attach a workspace file from this bot's home to the chat thread as an image (including .svg, .png, .jpg, .webp, .gif), video, audio or common file (.pdf, .md, .txt, .csv, .json, .zip). The file stays in place; users can open or play it from the message. Set present when the file is the result the user asked for, so it opens beside the conversation.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
path: { type: "string" },
|
||
present: {
|
||
type: "boolean",
|
||
description:
|
||
"True only for the finished result of the request, not for supporting files.",
|
||
},
|
||
},
|
||
required: ["path"],
|
||
},
|
||
},
|
||
{
|
||
name: "shell",
|
||
description:
|
||
"Run a command inside this bot's computer. cwd defaults to the bot's folder on a Team Computer and the workspace root on a Private Computer.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
command: { type: "string" },
|
||
cwd: { type: "string" },
|
||
},
|
||
required: ["command"],
|
||
},
|
||
},
|
||
{
|
||
name: "open_path",
|
||
description:
|
||
"Open a workspace file or an http(s) URL in its default graphical application on this bot's computer and return the resulting screen.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: { path: { type: "string" } },
|
||
required: ["path"],
|
||
},
|
||
},
|
||
{
|
||
name: "launch_app",
|
||
description:
|
||
"Launch an installed graphical application on this bot's computer, optionally with a URI, and return the resulting screen.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
application: { type: "string" },
|
||
uri: { type: "string" },
|
||
},
|
||
required: ["application"],
|
||
},
|
||
},
|
||
{
|
||
name: "request_takeover",
|
||
description:
|
||
"Ask the user to take over the computer screen for passwords, 2FA, CAPTCHA, payment, passkeys, or other protected input. Never ask the user to paste protected values in chat.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: { reason: { type: "string" } },
|
||
required: ["reason"],
|
||
},
|
||
},
|
||
{
|
||
name: "ask_user",
|
||
description:
|
||
"Ask the user one short multiple-choice question with tappable options, then wait for their selection. Use this instead of asking them to type when two to four concise choices are enough.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
question: { type: "string", maxLength: 240 },
|
||
options: {
|
||
type: "array",
|
||
items: { type: "string", minLength: 1, maxLength: 80 },
|
||
minItems: 2,
|
||
maxItems: 4,
|
||
uniqueItems: true,
|
||
},
|
||
},
|
||
required: ["question", "options"],
|
||
},
|
||
},
|
||
{
|
||
name: "show_card",
|
||
description:
|
||
'Show the user a card instead of a list in prose: a title, short facts, a servings stepper, a checklist they tick off, numbered steps with timers, options they tap, or up to three buttons. Use it for everyday things worth keeping in view. Templates: a recipe is title + meta ["30 min"] + stepper {label "Servings", value} + checklist of ingredients with amount and unit + steps with timer_minutes + start_label "Cook" + actions ["Add to shopping list", "Every week"]; a shopping list is a checklist; a day plan is steps starting with the time; a decision is facts plus options. A tap on an option or button arrives as the user\'s next message. Keep text short; the card is the answer, so do not repeat it in your reply. At most five parts including the title.',
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
title: { type: "string", maxLength: 120 },
|
||
subtitle: { type: "string", maxLength: 120 },
|
||
meta: {
|
||
type: "array",
|
||
description: 'Up to three short facts beside the title, such as "30 min".',
|
||
items: { type: "string", maxLength: 80 },
|
||
maxItems: 3,
|
||
},
|
||
facts: {
|
||
type: "array",
|
||
items: {
|
||
type: "object",
|
||
properties: {
|
||
label: { type: "string", maxLength: 80 },
|
||
value: { type: "string", maxLength: 200 },
|
||
},
|
||
required: ["label", "value"],
|
||
},
|
||
maxItems: 8,
|
||
},
|
||
stepper: {
|
||
type: "object",
|
||
description:
|
||
"A number the user turns up or down, such as servings. Checklist amounts are written for this value and scale with it.",
|
||
properties: {
|
||
label: { type: "string", maxLength: 80 },
|
||
value: { type: "integer", minimum: 1, maximum: 99 },
|
||
},
|
||
required: ["label", "value"],
|
||
},
|
||
checklist: {
|
||
type: "array",
|
||
description:
|
||
'Entries to tick off. An ingredient is { label: "Flour", amount: 200, unit: "g" }; a counted one is { label: "Egg", plural: "Eggs", amount: 1 }.',
|
||
items: {
|
||
anyOf: [
|
||
{ type: "string", minLength: 1, maxLength: 80 },
|
||
{
|
||
type: "object",
|
||
properties: {
|
||
label: { type: "string", minLength: 1, maxLength: 80 },
|
||
amount: { type: "number", exclusiveMinimum: 0 },
|
||
unit: { type: "string", maxLength: 20 },
|
||
plural: { type: "string", maxLength: 80 },
|
||
},
|
||
required: ["label"],
|
||
},
|
||
],
|
||
},
|
||
maxItems: 30,
|
||
},
|
||
steps: {
|
||
type: "array",
|
||
items: {
|
||
anyOf: [
|
||
{ type: "string", minLength: 1, maxLength: 300 },
|
||
{
|
||
type: "object",
|
||
properties: {
|
||
text: { type: "string", minLength: 1, maxLength: 300 },
|
||
timer_minutes: { type: "number", exclusiveMinimum: 0, maximum: 1440 },
|
||
},
|
||
required: ["text"],
|
||
},
|
||
],
|
||
},
|
||
maxItems: 20,
|
||
},
|
||
start_label: {
|
||
type: "string",
|
||
maxLength: 80,
|
||
description:
|
||
'A button that walks through the steps one at a time in full screen, such as "Cook".',
|
||
},
|
||
options: {
|
||
type: "array",
|
||
items: { type: "string", minLength: 1, maxLength: 80 },
|
||
minItems: 2,
|
||
maxItems: 6,
|
||
uniqueItems: true,
|
||
},
|
||
multiple: {
|
||
type: "boolean",
|
||
description: "Let the user pick several options and send them with an action.",
|
||
},
|
||
actions: {
|
||
type: "array",
|
||
items: { type: "string", minLength: 1, maxLength: 80 },
|
||
maxItems: 3,
|
||
uniqueItems: true,
|
||
},
|
||
},
|
||
required: ["title"],
|
||
},
|
||
},
|
||
{
|
||
name: "suggest",
|
||
description:
|
||
"Offer the user a ready-made action they start with one tap, instead of asking in text or doing it unasked: a routine, a note, a file, a task for a specialist. Name the exact tool and its arguments; nothing runs until the user taps Accept, and then it runs exactly as given. Use it when the user should decide, such as \"Should I do this every week?\". You can suggest several things in one reply. Results of a specialist's work that need the user's yes come back to you; suggest them with bot set to that specialist.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
tool: { type: "string", description: "The name of one of your tools." },
|
||
args: {
|
||
type: "object",
|
||
description: "That tool's arguments, exactly as you would call it.",
|
||
},
|
||
why: {
|
||
type: "string",
|
||
maxLength: 120,
|
||
description: "One short reason the user sees, in the user's language.",
|
||
},
|
||
},
|
||
required: ["tool", "args"],
|
||
},
|
||
},
|
||
{
|
||
name: "autopilot_start",
|
||
description:
|
||
"Keep working on the user's order on your own, run after run, with no time limit: when the user asks you to work through the night, keep going until something is done, or only hunt bugs for a while. Starts at once and shows a line with Stop in the conversation. Calling it again in this thread replaces the order.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
goal: { type: "string", maxLength: 500, description: "The order in the user's words." },
|
||
focus: {
|
||
type: "string",
|
||
maxLength: 200,
|
||
description: 'An optional limit the user set, such as "only find bugs, change nothing".',
|
||
},
|
||
until: {
|
||
type: "string",
|
||
description: "ISO datetime to stop at. Omit to work until the order is done.",
|
||
},
|
||
allow: {
|
||
type: "array",
|
||
items: { type: "string" },
|
||
description:
|
||
"Tool names the user explicitly allowed without asking while this runs, such as a commit tool. Only what the user said; the rest is parked for their approval.",
|
||
},
|
||
},
|
||
required: ["goal"],
|
||
},
|
||
},
|
||
{
|
||
name: "autopilot_checkpoint",
|
||
description:
|
||
"Report a finished step of the autopilot: what changed, and what comes next. Call it after every step; without it the autopilot pauses.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
summary: { type: "string", maxLength: 300, description: "What this step achieved." },
|
||
next: { type: "string", maxLength: 300, description: "The next step you plan." },
|
||
},
|
||
required: ["summary"],
|
||
},
|
||
},
|
||
{
|
||
name: "autopilot_finish",
|
||
description: "End the autopilot because the order is done, with the result for the user.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
result: {
|
||
type: "string",
|
||
maxLength: 2000,
|
||
description: "What was done, what is left, and what waits on the user.",
|
||
},
|
||
},
|
||
required: ["result"],
|
||
},
|
||
},
|
||
{
|
||
name: "autopilot_stop",
|
||
description:
|
||
'Stop the autopilot in this thread when the user asks you to ("stop", "that\'s enough").',
|
||
inputSchema: { type: "object", properties: {} },
|
||
},
|
||
{
|
||
name: "plan_create",
|
||
description:
|
||
"Create a live step-by-step execution plan in the conversation when a task breaks into 3 or more distinct subtasks. By default (await_approval: false), step 1 starts immediately and you execute the steps in this turn while updating progress with plan_update. Set await_approval: true only when the plan involves destructive, irreversible, or high-risk changes so the user can approve or adjust the plan before execution begins.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
title: {
|
||
type: "string",
|
||
maxLength: 160,
|
||
description: "Short objective title for the plan, in the user's language.",
|
||
},
|
||
steps: {
|
||
type: "array",
|
||
minItems: 2,
|
||
maxItems: 12,
|
||
items: {
|
||
anyOf: [
|
||
{ type: "string", minLength: 1, maxLength: 200 },
|
||
{
|
||
type: "object",
|
||
properties: {
|
||
label: { type: "string", minLength: 1, maxLength: 200 },
|
||
note: { type: "string", maxLength: 240 },
|
||
},
|
||
required: ["label"],
|
||
},
|
||
],
|
||
},
|
||
description: "Ordered subtasks (2 to 12 concise steps).",
|
||
},
|
||
await_approval: {
|
||
type: "boolean",
|
||
description:
|
||
"True only for high-risk, destructive, or far-reaching plans that require user sign-off before execution. False (default) to start executing immediately.",
|
||
},
|
||
approval_question: {
|
||
type: "string",
|
||
maxLength: 240,
|
||
description:
|
||
"Optional short question in the user's language when await_approval is true.",
|
||
},
|
||
approve_label: {
|
||
type: "string",
|
||
maxLength: 80,
|
||
description:
|
||
"Optional button label to start the plan when await_approval is true (e.g. 'Execute plan' / 'Plan ausführen').",
|
||
},
|
||
adjust_label: {
|
||
type: "string",
|
||
maxLength: 80,
|
||
description:
|
||
"Optional button label to adjust or cancel when await_approval is true (e.g. 'Adjust first' / 'Anpassen').",
|
||
},
|
||
},
|
||
required: ["title", "steps"],
|
||
},
|
||
},
|
||
{
|
||
name: "plan_update",
|
||
description:
|
||
"Update the live status of steps in the active execution plan as you complete each subtask and move to the next.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
plan_id: {
|
||
type: "string",
|
||
description:
|
||
"Optional planId from plan_create (defaults to the latest plan in this thread).",
|
||
},
|
||
steps: {
|
||
type: "array",
|
||
minItems: 1,
|
||
maxItems: 12,
|
||
items: {
|
||
type: "object",
|
||
properties: {
|
||
id: { type: "string", description: "Step id such as 's1', 's2'." },
|
||
index: {
|
||
type: "integer",
|
||
minimum: 1,
|
||
maximum: 12,
|
||
description: "1-based step number.",
|
||
},
|
||
status: {
|
||
type: "string",
|
||
enum: ["pending", "in_progress", "completed", "skipped"],
|
||
},
|
||
label: { type: "string", maxLength: 200 },
|
||
note: { type: "string", maxLength: 240 },
|
||
},
|
||
required: ["status"],
|
||
},
|
||
},
|
||
},
|
||
required: ["steps"],
|
||
},
|
||
},
|
||
{
|
||
name: "message_user",
|
||
description:
|
||
"Post a short progress update to the user in this chat immediately. Does not end your turn. Use sparingly during long work for high-signal beats (what you are checking, then a result). Do not dump tool logs, thinking, or a play-by-play of every call. HARD LIMIT: cut off silently at 500 characters, so never put your final answer, a report, or any long-form content here \u2014 it will arrive mangled and the user will never see the rest. Always write your complete final answer in your normal reply, not here.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
message: {
|
||
type: "string",
|
||
maxLength: 500,
|
||
description:
|
||
"Short user-visible update, not the final answer \u2014 longer text is silently truncated.",
|
||
},
|
||
},
|
||
required: ["message"],
|
||
},
|
||
},
|
||
{
|
||
name: "request_secret",
|
||
description:
|
||
"Collect a credential in a masked field. Supply credential to save a named API credential for this bot and user at one HTTPS origin, or connectionId for a one-use connector code. Existing named credentials are reused unless replace is true. For website logins, CAPTCHA, passkeys, or anything that needs the live desktop, call request_takeover instead.",
|
||
// Exactly one destination: credential XOR connectionId. Sibling optionals
|
||
// looked schema-valid to models but the executor rejects both and neither.
|
||
inputSchema: {
|
||
oneOf: [
|
||
{
|
||
type: "object",
|
||
properties: {
|
||
label: { type: "string" },
|
||
purpose: { type: "string", enum: SecretAskPurpose.options },
|
||
credential: z.toJSONSchema(BotSecretDestination),
|
||
replace: {
|
||
type: "boolean",
|
||
description: "Ask the user to replace an existing credential value.",
|
||
},
|
||
},
|
||
required: ["label", "purpose", "credential"],
|
||
additionalProperties: false,
|
||
},
|
||
{
|
||
type: "object",
|
||
properties: {
|
||
label: { type: "string" },
|
||
purpose: { type: "string", enum: SecretAskPurpose.options },
|
||
connectionId: { type: "string" },
|
||
},
|
||
required: ["label", "purpose", "connectionId"],
|
||
additionalProperties: false,
|
||
},
|
||
],
|
||
},
|
||
},
|
||
{
|
||
name: "list_secrets",
|
||
description:
|
||
"List saved credential names and destinations available to this bot and user. Values are never returned.",
|
||
inputSchema: { type: "object", properties: {} },
|
||
},
|
||
{
|
||
name: "secret_request",
|
||
description:
|
||
"Make an authenticated HTTPS request using a saved credential name. The backend injects authentication only at its saved origin. Redirects are rejected; response echoes of the credential are redacted. Use normal request URLs and bodies without secret placeholders.",
|
||
inputSchema: z.toJSONSchema(SecretHttpRequest, { io: "input" }),
|
||
},
|
||
{
|
||
name: "forget_secret",
|
||
description:
|
||
"Remove a saved credential for this bot and user, preventing future requests from using it.",
|
||
inputSchema: z.toJSONSchema(z.object({ name: BotSecretName })),
|
||
},
|
||
{
|
||
name: "render_plot",
|
||
description:
|
||
'Render a chart from tabular data as a PNG and attach it to the chat. Backed by Observable Plot: bar, line, area, scatter, histogram, heatmap, box plot, facets, and more via a declarative JSON spec. Call with {"charts": true} FIRST to list every chart type with a complete runnable example spec ({"charts": "<keyword>"} searches), then copy the closest example and substitute your rows and columns. {"help": true} returns the full guide. Pass rows inline as data, or data_path for a .csv/.tsv/.json file in your home.',
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
charts: {
|
||
description:
|
||
'true lists all chart types with runnable example specs; a keyword string (e.g. "distribution", "share", "trend") searches them.',
|
||
},
|
||
help: {
|
||
type: "boolean",
|
||
description: "Return the full render_plot skill guide instead of rendering.",
|
||
},
|
||
spec: {
|
||
type: "object",
|
||
description:
|
||
"Declarative Observable Plot spec: {title?, width?, height?, x?, y?, color?, fx?, fy?, marks: [{type, options, transform?, data?}]}.",
|
||
},
|
||
data: {
|
||
type: "array",
|
||
description: "Rows as objects, shared by marks without their own data.",
|
||
},
|
||
data_path: {
|
||
type: "string",
|
||
description:
|
||
"Workspace path of a .csv, .tsv, or .json rows file to load instead of inline data.",
|
||
},
|
||
path: {
|
||
type: "string",
|
||
description: "Output PNG path in this bot's home. Default charts/plot-<n>.png.",
|
||
},
|
||
attach: {
|
||
type: "boolean",
|
||
description: "Attach the rendered PNG to the chat (default true).",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
{
|
||
name: "generate_image",
|
||
description:
|
||
"Generate up to 4 images from a prompt on the self-hosted ComfyUI server, or rework a source image (image-to-image). Use this for logos, signets, monograms, brand marks, packaging/signage mockups, key visuals, moodboards, and photography. For logos and emblems, prompt for high-contrast flat vector marks, negative-space geometry, or luxury tactile marks on a pure solid background; for photography, specify tactile materials, camera/lens, lighting mood, and palette. Set continue_after true when you need the rendered files in your workspace (attachments/<artifactId>.<ext>) for vector tracing, a brand book, a website, or a next step.",
|
||
inputSchema: mediaParameters(GenerateImageInputSchema),
|
||
},
|
||
{
|
||
name: "generate_video",
|
||
description:
|
||
"Generate a short video clip from a prompt on the self-hosted ComfyUI server, optionally starting from a generated or attached image (source_artifact_id). Describe subject motion, camera movement, atmosphere, and lighting; set continue_after true when you need the clip for editing or embedding.",
|
||
inputSchema: mediaParameters(GenerateVideoInputSchema),
|
||
},
|
||
{
|
||
name: "edit_image",
|
||
description:
|
||
"Edit an image artifact into a new one: crop, rotate, flip, resize (crop, pad, or blur to fit a format), add borders, convert the format, or repaint it from a prompt (only when generate_image is available). Edits apply in that order.",
|
||
inputSchema: mediaParameters(EditImageInputSchema),
|
||
},
|
||
{
|
||
name: "edit_video",
|
||
description: `Edit a video artifact into a new one. steps run in order, times in seconds on the result of the steps before: trim {start, end}; keep {segments: [{start, end}]} keeps those parts and cuts the rest; concat {artifact_ids, at "end" or "start"} adds videos at the end, or at the start for an intro, fitted to the current frame; reframe {preset, fit}; speed {factor 0.25–4}; volume {gain 0–8, 1 unchanged}; mute; subtitles {artifact_id} burns in an SRT or VTT file; extract_audio (last step) makes the result M4A audio; poster {at} picks the cover frame, or as the only step saves that frame as an image; remove_silence {threshold_db -35, min_silence 0.6, padding 0.12} cuts pauses quieter than threshold_db dB and at least min_silence s long, keeping padding s on each side; overlay {artifact_id, position top_left|top_right|bottom_left|bottom_right|center, width 0.15, opacity 1, start, end} lays a PNG, JPEG or WebP image such as a logo or watermark over the video, width as a share of the frame, from start to end s or the whole time; top_right stays clear of 9:16 app buttons. ${VIDEO_PRESETS_AND_FITS} Videos come out as H.264/AAC MP4. title: a few words in the user's language for the card and file name.`,
|
||
inputSchema: mediaParameters(EditVideoInputSchema),
|
||
},
|
||
{
|
||
name: "export_video",
|
||
description: `Export a video artifact as one H.264/AAC MP4 per platform and per preset, 6 at most. When the user names where it will be posted, use platforms: each file is fitted to that platform's frame, frame rate, bitrate and loudness, then checked. Platforms (longest video): ${VIDEO_PLATFORMS}. A platform the video is too long or too short for is skipped: trim with edit_video first. ${VIDEO_PRESETS_AND_FITS} For cuts, speed, audio, subtitles or a logo use edit_video.`,
|
||
inputSchema: mediaParameters(ExportVideoInputSchema),
|
||
},
|
||
{
|
||
name: "transcribe",
|
||
description:
|
||
"Transcribe speech in an audio or video artifact into SRT and VTT subtitle files, shown on a media card. Set continue_after to get the transcript back, e.g. to burn the .srt in with edit_video.",
|
||
inputSchema: mediaParameters(TranscribeInputSchema),
|
||
},
|
||
{
|
||
name: "analyze_video",
|
||
description:
|
||
"Find cut points in a video artifact: scene changes, pauses, loud moments and, if it was transcribed, transcript timing. Read-only; you are woken with the result as JSON (times in seconds) to plan edit_video steps. title: a few words in the user's language for the card.",
|
||
inputSchema: mediaParameters(AnalyzeVideoInputSchema, { continueAfter: false }),
|
||
},
|
||
{
|
||
name: "search_media",
|
||
description:
|
||
"Search the user's photo and video library, newest first. query matches words in folder and file names (trips, events, dates people put in names). Returns asset ids; import_media brings one into the conversation.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
query: { type: "string" },
|
||
kind: { type: "string", enum: ["photo", "video"] },
|
||
taken_after: { type: "string", description: "ISO 8601 date-time." },
|
||
taken_before: { type: "string", description: "ISO 8601 date-time." },
|
||
limit: { type: "integer", minimum: 1, maximum: 50, description: "Default 20." },
|
||
},
|
||
},
|
||
},
|
||
{
|
||
name: "import_media",
|
||
description:
|
||
"Copy a photo or video from the user's library into this conversation as an artifact, so it can be shown or edited with the media tools. The original file is not changed.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: { asset_id: { type: "string" } },
|
||
required: ["asset_id"],
|
||
},
|
||
},
|
||
{
|
||
name: "add_mcp_server",
|
||
description:
|
||
"Connect an MCP tool server to this Space when the user asks you to add one and provides the details (URL or command, optional token/headers/env). The server is created immediately and assigned to you. If it needs browser OAuth authorization, an approval card appears in the chat for the user to complete — tell them to click Authorize. Do not invent endpoints; only use details the user provided.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
name: { type: "string", description: 'Display name, e.g. "Brex".' },
|
||
transport: {
|
||
type: "string",
|
||
enum: ["streamable_http", "sse", "stdio"],
|
||
description:
|
||
"streamable_http for modern HTTP servers, sse for legacy HTTP servers, stdio for local commands.",
|
||
},
|
||
endpoint: {
|
||
type: "string",
|
||
description: "HTTPS URL of the remote MCP server (required unless transport is stdio).",
|
||
},
|
||
command: {
|
||
type: "string",
|
||
description:
|
||
"Executable path for stdio transport (required for stdio). Must be allowlisted by the deployment.",
|
||
},
|
||
args: {
|
||
type: "array",
|
||
items: { type: "string" },
|
||
description:
|
||
"Arguments for the stdio command. A single space-separated string also works.",
|
||
},
|
||
env: {
|
||
type: "object",
|
||
description: 'Environment variables for stdio transport, e.g. {"API_KEY": "..."}.',
|
||
},
|
||
headers: {
|
||
type: "object",
|
||
description: 'HTTP headers for remote transports, e.g. {"Authorization": "Bearer ..."}.',
|
||
},
|
||
secret: {
|
||
type: "string",
|
||
description: "Static access token, equivalent to an Authorization: Bearer header.",
|
||
},
|
||
assign_to_self: {
|
||
type: "boolean",
|
||
description:
|
||
"Assign the server to you so its tools are usable in this conversation (default true).",
|
||
},
|
||
},
|
||
required: ["name", "transport"],
|
||
},
|
||
},
|
||
{
|
||
name: "remember",
|
||
description:
|
||
"Add a durable note to this bot's memory document; it is appended with today's date. Use mode replace only to rewrite the whole document, e.g. to condense it when it is full.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
content: { type: "string" },
|
||
path: { type: "string", description: "Memory document, default MEMORY.md." },
|
||
mode: {
|
||
type: "string",
|
||
enum: ["append", "replace"],
|
||
description: "append (default) adds a note; replace sets the whole document to content.",
|
||
},
|
||
},
|
||
required: ["content"],
|
||
},
|
||
},
|
||
{
|
||
name: "update_user_profile",
|
||
description:
|
||
"Save what you learned about the user so all of their bots can use it: short facts and profile settings. Save only what the user said or clearly confirmed, never guesses. Health and values facts stay hidden from bots until the user confirms them.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
facts: {
|
||
type: "array",
|
||
maxItems: 20,
|
||
items: {
|
||
type: "object",
|
||
properties: {
|
||
category: { type: "string", enum: [...USER_FACT_CATEGORIES] },
|
||
value: {
|
||
type: "string",
|
||
description: 'One short statement, e.g. "Works night shifts as a nurse".',
|
||
},
|
||
},
|
||
required: ["category", "value"],
|
||
},
|
||
},
|
||
time_zone: { type: "string", description: "IANA time zone, e.g. Europe/Vienna." },
|
||
language: {
|
||
type: "string",
|
||
description: "Language the user wants replies in, as a BCP-47 tag, e.g. de.",
|
||
},
|
||
communication_style: {
|
||
type: "string",
|
||
description: 'How the user wants to be talked to, e.g. "brief and informal".',
|
||
},
|
||
},
|
||
},
|
||
},
|
||
...coachTools,
|
||
{
|
||
name: "notify_user",
|
||
description:
|
||
"Send the user a short push notification that opens the main conversation. Use it sparingly, for a check-in or reminder that matters now, and say the same in the conversation. The user's quiet hours, their nudge setting and a daily limit apply; the result says whether it was sent.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
body: {
|
||
type: "string",
|
||
maxLength: NOTIFY_USER_BODY_MAX_LENGTH,
|
||
description: "One or two short sentences.",
|
||
},
|
||
title: {
|
||
type: "string",
|
||
maxLength: NOTIFY_USER_TITLE_MAX_LENGTH,
|
||
description: "Optional; defaults to your name.",
|
||
},
|
||
},
|
||
required: ["body"],
|
||
},
|
||
},
|
||
{
|
||
name: "web_search",
|
||
description:
|
||
"Search the public web. Returns titles, URLs, and snippets. Pass queries (up to 3 parallel search angles) for multi-angle research, category ('web', 'academic' for papers on arXiv/Scholar/PubMed, or 'discussions' for Reddit/HN), or domains to restrict results to specific sites. Follow with web_fetch to read pages. Does not need a computer.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
query: { type: "string", description: "Primary search query." },
|
||
queries: {
|
||
type: "array",
|
||
items: { type: "string" },
|
||
maxItems: 3,
|
||
description:
|
||
"Optional batch of up to 3 parallel search queries covering distinct angles; results are merged and deduplicated.",
|
||
},
|
||
category: {
|
||
type: "string",
|
||
enum: ["web", "academic", "discussions"],
|
||
description:
|
||
"Search focus: 'web' (default), 'academic' (papers/studies), or 'discussions' (community forums).",
|
||
},
|
||
domains: {
|
||
type: "array",
|
||
items: { type: "string" },
|
||
description:
|
||
"Optional domain filter (e.g. ['arxiv.org', 'docs.rs']) to restrict results to specific websites.",
|
||
},
|
||
maxResults: {
|
||
type: "number",
|
||
description: "Max results to return (default 5, max 10).",
|
||
},
|
||
},
|
||
},
|
||
readOnly: true,
|
||
},
|
||
{
|
||
name: "web_fetch",
|
||
description:
|
||
"Fetch one or up to 3 public http(s) pages in parallel and return readable text (with HTML tables preserved as Markdown). Pass query when looking for specific facts, benchmarks, or specs on a long page so the relevant sections and numbers are extracted instead of cut off. Read-only; no JavaScript.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
url: { type: "string", description: "Page URL (http or https)." },
|
||
urls: {
|
||
type: "array",
|
||
items: { type: "string" },
|
||
maxItems: 3,
|
||
description: "Optional batch of up to 3 page URLs to fetch in parallel.",
|
||
},
|
||
query: {
|
||
type: "string",
|
||
description:
|
||
"Optional focus query: extracts the sections, tables, and numerical facts relevant to this query from long pages.",
|
||
},
|
||
maxChars: {
|
||
type: "number",
|
||
description: "Max characters of body text per page (default 8000).",
|
||
},
|
||
},
|
||
},
|
||
readOnly: true,
|
||
},
|
||
{
|
||
name: "cloud_agent_launch",
|
||
description:
|
||
"Launch a remote cloud coding agent on a connected repository. Returns immediately with a tracking id and status; the agent link appears when launched. Opens a PR when openPr is true. Not the bot computer.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
prompt: { type: "string", description: "Task for the remote agent." },
|
||
repository: {
|
||
type: "string",
|
||
description: "Git repository URL (optional for no-repo agents).",
|
||
},
|
||
openPr: {
|
||
type: "boolean",
|
||
description: "Open a pull request when the run finishes.",
|
||
},
|
||
images: {
|
||
type: "array",
|
||
description: "Optional images (data+mimeType or url).",
|
||
items: { type: "object" },
|
||
},
|
||
},
|
||
required: ["prompt"],
|
||
},
|
||
},
|
||
{
|
||
name: "cloud_agent_status",
|
||
description: "Get status, branch, and PR url for a cloud coding agent.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
id: { type: "string", description: "Cloud agent id from launch." },
|
||
},
|
||
required: ["id"],
|
||
},
|
||
readOnly: true,
|
||
},
|
||
{
|
||
name: "cloud_agent_reply",
|
||
description: "Send a follow-up prompt to an existing cloud coding agent.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
id: { type: "string", description: "Cloud agent id." },
|
||
prompt: { type: "string", description: "Follow-up instruction." },
|
||
images: {
|
||
type: "array",
|
||
description: "Optional images (data+mimeType or url).",
|
||
items: { type: "object" },
|
||
},
|
||
},
|
||
required: ["id", "prompt"],
|
||
},
|
||
},
|
||
{
|
||
name: "cloud_agent_cancel",
|
||
description: "Cancel the active run on a cloud coding agent.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
id: { type: "string", description: "Cloud agent id." },
|
||
},
|
||
required: ["id"],
|
||
},
|
||
},
|
||
// Semantic-memory tools: exposed by selectMemoryTools() only when a
|
||
// A Space memory provider is configured (which hides `remember`).
|
||
{
|
||
name: "save_memory",
|
||
description:
|
||
"Store a durable fact in this bot's semantic memory (preferences, decisions, recurring context). Use for anything worth recalling in future conversations.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
content: { type: "string" },
|
||
},
|
||
required: ["content"],
|
||
},
|
||
},
|
||
{
|
||
name: "recall_memory",
|
||
description: "Semantically search this bot's durable memory for facts relevant to a query.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
query: { type: "string" },
|
||
},
|
||
required: ["query"],
|
||
},
|
||
},
|
||
{
|
||
name: "forget_memory",
|
||
description:
|
||
"Forget a durable semantic memory by id (from recall citations). Providers without forget support return an error.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
id: { type: "string", description: "Memory id from a prior recall citation." },
|
||
entity: {
|
||
type: "string",
|
||
description:
|
||
"Optional entity/namespace from the recall citation when the provider scopes deletes.",
|
||
},
|
||
reason: { type: "string", description: "Optional reason recorded with the forget." },
|
||
},
|
||
required: ["id"],
|
||
},
|
||
},
|
||
{
|
||
name: "scratchpad_list",
|
||
description:
|
||
"List this bot's scratchpad / open-work items (todos and parked work). By default omits completed items.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
includeDone: {
|
||
type: "boolean",
|
||
description: "When true, include completed items.",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
{
|
||
name: "scratchpad_add",
|
||
description:
|
||
"Add an open-work item to this bot's scratchpad. Use for todos or parked work that should outlive this turn. Not a reminder or schedule.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
title: { type: "string", description: "Short title for the item." },
|
||
status: {
|
||
type: "string",
|
||
enum: ["open", "parked", "done"],
|
||
description: "Defaults to open.",
|
||
},
|
||
notes: { type: "string", description: "Optional notes." },
|
||
},
|
||
required: ["title"],
|
||
},
|
||
},
|
||
{
|
||
name: "scratchpad_update",
|
||
description: "Update a scratchpad item's title, status, or notes.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
itemId: { type: "string" },
|
||
title: { type: "string" },
|
||
status: { type: "string", enum: ["open", "parked", "done"] },
|
||
notes: { type: "string" },
|
||
},
|
||
required: ["itemId"],
|
||
},
|
||
},
|
||
{
|
||
name: "scratchpad_complete",
|
||
description: "Mark a scratchpad item done.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
itemId: { type: "string" },
|
||
},
|
||
required: ["itemId"],
|
||
},
|
||
},
|
||
{
|
||
name: "scratchpad_remove",
|
||
description: "Permanently remove a scratchpad item.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
itemId: { type: "string" },
|
||
},
|
||
required: ["itemId"],
|
||
},
|
||
},
|
||
{
|
||
name: "schedule_create",
|
||
description:
|
||
'Create a reminder or recurring job for this bot. Use it whenever the user describes something that should happen later or again, even in passing ("remind me in 10 minutes", "every Sunday I plan the week"): create it right away instead of asking first; the user sees it in the conversation and can undo it. Never use it for work the user wants now, however long: do that work in this turn. Without a time, use 08:00 for mornings, 19:00 for evenings and 09:00 otherwise. Reusing the name of an active repeating schedule changes that schedule instead of adding another. Repeats: cron or every/unit (min 1 minute). One-shot: runAt, delayMinutes, or delaySeconds.',
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
name: { type: "string", description: "Short label shown in Routines." },
|
||
prompt: {
|
||
type: "string",
|
||
description:
|
||
"Self-contained instructions for when the schedule fires. That run sees none of this conversation, so include every detail it needs: what to check or do, which connected app or skill to use (with exact names such as owner/repo), what to report, and when to say nothing. Prefer connected apps over the computer browser or web search for app data.",
|
||
},
|
||
cron: { type: "string", description: "5-field cron for repeating schedules." },
|
||
every: { type: "number", description: "Repeat interval amount for repeating schedules." },
|
||
unit: {
|
||
type: "string",
|
||
enum: ["minutes", "hours", "days"],
|
||
description: "Unit for every (minimum 1 minute).",
|
||
},
|
||
runAt: {
|
||
type: "string",
|
||
description: "ISO datetime for a one-shot schedule.",
|
||
},
|
||
delayMinutes: {
|
||
type: "number",
|
||
description: "Minutes from now for a one-shot schedule.",
|
||
},
|
||
delaySeconds: {
|
||
type: "number",
|
||
description: "Seconds from now for a one-shot schedule (may be under one minute).",
|
||
},
|
||
timezone: {
|
||
type: "string",
|
||
description: "IANA timezone (default: the user's time zone, else UTC).",
|
||
},
|
||
},
|
||
required: ["name", "prompt"],
|
||
},
|
||
},
|
||
{
|
||
name: "schedule_list",
|
||
description: "List this bot's active and inactive schedules (routines).",
|
||
inputSchema: { type: "object", properties: {} },
|
||
},
|
||
{
|
||
name: "schedule_cancel",
|
||
description: "Cancel a schedule by routineId or exact name.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
routineId: { type: "string" },
|
||
name: { type: "string" },
|
||
},
|
||
},
|
||
},
|
||
{
|
||
name: "skill_read",
|
||
description:
|
||
"Load a skill (SKILL.md recipe) by its exact name from the catalog, then follow it right away.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
name: { type: "string", description: "Exact skill name from the catalog." },
|
||
},
|
||
required: ["name"],
|
||
},
|
||
},
|
||
{
|
||
name: "skill_create",
|
||
description:
|
||
"Create one of the user's skills: a reusable how-to (SKILL.md) available in all their projects. Use it when a multi-step task is worth repeating, the user asks to save a skill, or a skill_install skipped one. Keep account names, channels and inboxes out; those belong in a routine.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
name: { type: "string", description: "Short skill name." },
|
||
description: {
|
||
type: "string",
|
||
description: "When to use this skill (shown in the / picker and used for auto-use).",
|
||
},
|
||
body: {
|
||
type: "string",
|
||
description: "Markdown steps and guidance after the frontmatter.",
|
||
},
|
||
content: {
|
||
type: "string",
|
||
description:
|
||
"Optional full SKILL.md (frontmatter + body) instead of name/description/body.",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
{
|
||
name: "skill_install",
|
||
description:
|
||
"Install skills from a public GitHub link the user gave: a repository, a folder, or one SKILL.md. Every SKILL.md found becomes one of the user's own skills in all their projects. Call it once per link; the result lists installed and skipped skills.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
url: { type: "string", description: "The github.com link the user gave." },
|
||
},
|
||
required: ["url"],
|
||
},
|
||
},
|
||
{
|
||
name: "skill_update",
|
||
description:
|
||
"Update a user-created skill by name or id. Builtin and plugin skills are read-only.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
name: { type: "string", description: "Current exact skill name." },
|
||
skillId: { type: "string" },
|
||
newName: { type: "string" },
|
||
description: { type: "string" },
|
||
body: { type: "string" },
|
||
content: { type: "string", description: "Optional full replacement SKILL.md." },
|
||
},
|
||
},
|
||
},
|
||
{
|
||
name: "skill_delete",
|
||
description:
|
||
"Delete a user-created skill by name or id. Builtin and plugin skills cannot be deleted.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
name: { type: "string" },
|
||
skillId: { type: "string" },
|
||
},
|
||
},
|
||
},
|
||
{
|
||
name: "run_subagent",
|
||
description:
|
||
"Run a short-lived helper inside this turn only. It is not a bot: no list entry, no thread, no computer of its own, and it disappears when this turn ends. Never call this because the user asked to create a bot — that is spawn_bot, and spawn_bot alone.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
name: {
|
||
type: "string",
|
||
description: "Short label shown in the thread, e.g. scout or reviewer.",
|
||
},
|
||
task: { type: "string", description: "The work the helper should complete." },
|
||
instructions: {
|
||
type: "string",
|
||
description: "Optional extra system instructions for the helper.",
|
||
},
|
||
model_provider: {
|
||
type: "string",
|
||
description: "Optional connected model provider. Set together with model_id.",
|
||
},
|
||
model_id: {
|
||
type: "string",
|
||
description: "Optional connected model ID. Set together with model_provider.",
|
||
},
|
||
model_tier: {
|
||
type: "string",
|
||
enum: [...MODEL_TIERS],
|
||
description: SUBAGENT_MODEL_TIER_DESCRIPTION,
|
||
},
|
||
},
|
||
required: ["name", "task"],
|
||
},
|
||
},
|
||
{
|
||
name: "create_project",
|
||
description:
|
||
"Create a project group when the user asks for a new project or team workspace. Projects are shared chats for 2–6 agents. Always include yourself plus the relevant teammates in bot_ids. Creating the project is the whole action unless the user also asked to start work in it.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
name: {
|
||
type: "string",
|
||
minLength: 1,
|
||
maxLength: 80,
|
||
description: 'Short project name, e.g. "Website Redesign".',
|
||
},
|
||
bot_ids: {
|
||
type: "array",
|
||
items: { type: "string" },
|
||
minItems: 1,
|
||
description:
|
||
"Agent ids to invite. You are added automatically; include at least one other teammate.",
|
||
},
|
||
},
|
||
required: ["name", "bot_ids"],
|
||
},
|
||
},
|
||
{
|
||
name: "create_space",
|
||
description:
|
||
"Propose a new space in the current organization when the user asks for a separate data boundary. A space can contain many bots and groups, but its chats, files, memory, tools, and integrations stay isolated from other spaces. This always shows the user a confirmation card before creation. Creating the space is the whole action; do not create bots in it unless the user asks later.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
name: {
|
||
type: "string",
|
||
minLength: 1,
|
||
maxLength: 60,
|
||
description: 'Short display name, e.g. "Customer support".',
|
||
},
|
||
},
|
||
required: ["name"],
|
||
},
|
||
},
|
||
{
|
||
name: "spawn_bot",
|
||
description:
|
||
"Create a full, regular bot — the same kind the user creates from the + button. It gets its own thread, computer, and memory, and appears as a peer in the bot list. Do not also call run_subagent. Creating the bot is the whole action. Only set prompt if the user asked that new bot to start work immediately.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
name: { type: "string" },
|
||
title: { type: "string" },
|
||
instructions: { type: "string" },
|
||
prompt: {
|
||
type: "string",
|
||
description: "Optional first task to run in the new bot's thread.",
|
||
},
|
||
computer_mode: {
|
||
type: "string",
|
||
enum: ["team", "dedicated"],
|
||
description:
|
||
"Optional. team shares one screen with other Team bots; dedicated (Private) gets its own. Defaults to team.",
|
||
},
|
||
},
|
||
required: ["name"],
|
||
},
|
||
},
|
||
{
|
||
name: "update_bot",
|
||
description:
|
||
"Update this bot's own profile fields that the user sees in chat: name (header and list label), title (short role line), description, Oo look, speaking voice, and whether replies are read aloud. Call this when the user asks you to rename yourself, change your title/description or look, pick a different voice (e.g. Eva, Thorsten), or turn speaking on/off. Do not claim you updated the profile without calling this tool.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
name: {
|
||
type: "string",
|
||
description: "Display name shown in the chat header and bot list.",
|
||
},
|
||
title: {
|
||
type: "string",
|
||
description: "Short role or headline shown in bot settings.",
|
||
},
|
||
description: {
|
||
type: "string",
|
||
description: "Longer blurb describing what this bot does.",
|
||
},
|
||
look: {
|
||
type: "string",
|
||
description: `Oo avatar look. ${ooLookFormat()} To offer a look instead of switching on your own, wrap this call in suggest so the user sees a preview and accepts it.`,
|
||
},
|
||
voice_id: {
|
||
type: "string",
|
||
description:
|
||
"Voice id from the connected provider (e.g. dm_thorsten, af_heart). Match the user's requested voice name.",
|
||
},
|
||
auto_speak: {
|
||
type: "boolean",
|
||
description: "When true, read replies aloud. When false, stay silent.",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
{
|
||
name: "archive_bot",
|
||
description:
|
||
"Archive a bot this bot created. Archiving stops its work and routines, hides it from the active list, and preserves its conversation, memory, and files for the user to restore or delete later. confirm_name must exactly match its name. This cannot archive you, bots the user created, or bots another bot created.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
confirm_name: { type: "string", description: "Exact current name of the bot to archive." },
|
||
bot_id: {
|
||
type: "string",
|
||
description:
|
||
"Optional bot id. If omitted, the unique bot this bot created with confirm_name is archived.",
|
||
},
|
||
},
|
||
required: ["confirm_name"],
|
||
},
|
||
},
|
||
{
|
||
name: "message_bot",
|
||
description:
|
||
'Send a useful update, question, or result to another of the user\'s bots. You must call this tool to actually deliver it — writing the message in your own reply text (e.g. "[to Comms] ...") does not send anything and the recipient never sees it. Delivery is async and does not end your turn. Continue independent work; do not poll or send ack-only messages. Later updates only if they add something new.',
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
bot_id: { type: "string", description: "Target bot id from your teammate list." },
|
||
confirm_name: {
|
||
type: "string",
|
||
description: "Exact name of the target bot when bot_id is omitted.",
|
||
},
|
||
message: { type: "string", description: "What to send." },
|
||
intent: {
|
||
type: "string",
|
||
enum: ["request", "result", "question", "status", "fyi"],
|
||
description: "What the recipient should do with this message. Defaults to request.",
|
||
},
|
||
},
|
||
required: ["message"],
|
||
},
|
||
},
|
||
{
|
||
name: "handoff_to_bot",
|
||
description:
|
||
"In a group chat only: transfer a genuinely distinct next stage to another current member. Appends a visible handoff and starts that bot asynchronously. Do not hand a stage back merely to report or repeat the same work; post results in the shared thread. Never paste this tool as JSON in chat — invoke it directly.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
bot_id: { type: "string", description: "Target member bot id." },
|
||
confirm_name: {
|
||
type: "string",
|
||
description: "Exact name of the target member when bot_id is omitted.",
|
||
},
|
||
message: { type: "string", description: "What the receiving bot should do next." },
|
||
},
|
||
required: ["message"],
|
||
},
|
||
},
|
||
{
|
||
name: "delegate_task",
|
||
description:
|
||
"Main agent only: hand a distinct piece of work to one of the user's specialists as a tracked task. A task card appears in the main conversation; a specialist from this space joins it and works there where the user can follow, one from another space works in its own chat. You are woken in the main conversation with the result to close the task with the user. Does not end your turn; do not poll.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
bot: { type: "string", description: "Specialist name or id." },
|
||
title: {
|
||
type: "string",
|
||
maxLength: 120,
|
||
description: "Short task title shown on the card.",
|
||
},
|
||
brief: {
|
||
type: "string",
|
||
description:
|
||
"Everything the specialist needs: goal, context, constraints, and what to return.",
|
||
},
|
||
deadline: {
|
||
type: "string",
|
||
description: "Optional ISO 8601 date-time the result is needed by.",
|
||
},
|
||
},
|
||
required: ["bot", "title", "brief"],
|
||
},
|
||
},
|
||
];
|
||
|
||
/** Agent-connection tools, exposed only when the messaging surface is enabled. */
|
||
export const agentConnectionTools: ConnectorTool[] = [
|
||
{
|
||
name: "connect_agent",
|
||
description:
|
||
"Request a standing connection to another person's agent by their owner's chat address. The other owner must approve before either agent can message the other. Only for agents whose owner messaged the deployment's chat line.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
address: {
|
||
type: "string",
|
||
description:
|
||
"The owner's address on the chat surface: an E.164 phone number (e.g. +15551234567) or platform user id.",
|
||
},
|
||
},
|
||
required: ["address"],
|
||
},
|
||
},
|
||
{
|
||
name: "respond_agent_connection",
|
||
description:
|
||
"Approve or decline the newest pending agent connection request addressed to you. Use only on your owner's explicit instruction.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
accept: { type: "boolean", description: "true to approve, false to decline." },
|
||
},
|
||
required: ["accept"],
|
||
},
|
||
},
|
||
{
|
||
name: "message_agent",
|
||
description:
|
||
"Send a useful update, question, or result to another person's agent over an approved connection. Delivery is async and does not end your turn. Continue independent work; do not poll or send ack-only messages.",
|
||
inputSchema: {
|
||
type: "object",
|
||
properties: {
|
||
address: {
|
||
type: "string",
|
||
description:
|
||
"Chat address (phone number or platform user id) of the connected agent's owner.",
|
||
},
|
||
message: { type: "string", description: "What to send." },
|
||
},
|
||
required: ["address", "message"],
|
||
},
|
||
},
|
||
];
|