heyhank 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +83 -10
  3. package/bin/cli.ts +7 -7
  4. package/bin/ctl.ts +42 -42
  5. package/dist/assets/{AgentsPage-BPhirnCe.js → AgentsPage-B-AAmsMK.js} +3 -3
  6. package/dist/assets/AssistantPage-BV1Mfwdt.js +2 -0
  7. package/dist/assets/BusinessPage-tLpNEz19.js +1 -0
  8. package/dist/assets/{CronManager-DDbz-yiT.js → CronManager-B-K_n3Jg.js} +1 -1
  9. package/dist/assets/HelpPage-Bhf_j6Xr.js +1 -0
  10. package/dist/assets/{IntegrationsPage-CrOitCmJ.js → IntegrationsPage-DAMjs9tM.js} +1 -1
  11. package/dist/assets/JarvisHUD-C_TGXCCn.js +120 -0
  12. package/dist/assets/MediaPage-C48HTTrt.js +1 -0
  13. package/dist/assets/MemoryPage-JkC-qtgp.js +1 -0
  14. package/dist/assets/{PlatformDashboard-Do6F0O2p.js → PlatformDashboard-AUo7tNnE.js} +1 -1
  15. package/dist/assets/{Playground-Fc5cdc5p.js → Playground-AzNMsRBL.js} +1 -1
  16. package/dist/assets/{ProcessPanel-CslEiZkI.js → ProcessPanel-DpE_2sX3.js} +1 -1
  17. package/dist/assets/{PromptsPage-D2EhsdNO.js → PromptsPage-C2RQOs6p.js} +2 -2
  18. package/dist/assets/RunsPage-B9UOyO79.js +1 -0
  19. package/dist/assets/{SandboxManager-a1AVI5q2.js → SandboxManager-jHvYjwfh.js} +1 -1
  20. package/dist/assets/SettingsPage-BBJax6gt.js +51 -0
  21. package/dist/assets/SkillsMarketplace-IjmjfdjD.js +1 -0
  22. package/dist/assets/SocialMediaPage-DoPZHhr2.js +10 -0
  23. package/dist/assets/{TailscalePage-CHiFhZXF.js → TailscalePage-DDEY7ckO.js} +1 -1
  24. package/dist/assets/TelephonyPage-OPNBZYKt.js +9 -0
  25. package/dist/assets/{TerminalPage-Drwyrnfd.js → TerminalPage-BjMbHHW3.js} +1 -1
  26. package/dist/assets/{gemini-live-client-C7rqAW7G.js → gemini-live-client-C70FEtX2.js} +11 -8
  27. package/dist/assets/{index-CEqZnThB.js → index-BgYM4wXw.js} +94 -93
  28. package/dist/assets/index-BkjSoVgn.css +32 -0
  29. package/dist/assets/sw-register-C7NOHtIu.js +1 -0
  30. package/dist/assets/text-chat-client-BSbLJerZ.js +2 -0
  31. package/dist/index.html +2 -2
  32. package/dist/sw.js +1 -1
  33. package/package.json +6 -1
  34. package/server/agent-executor.ts +37 -2
  35. package/server/agent-store.ts +3 -3
  36. package/server/agent-types.ts +11 -0
  37. package/server/assistant-store.ts +232 -6
  38. package/server/auth-manager.ts +9 -0
  39. package/server/cache-headers.ts +1 -1
  40. package/server/calendar-service.ts +10 -0
  41. package/server/ceo/document-store.ts +129 -0
  42. package/server/ceo/finance-store.ts +343 -0
  43. package/server/ceo/kpi-store.ts +208 -0
  44. package/server/ceo/memory-import.ts +277 -0
  45. package/server/ceo/news-store.ts +208 -0
  46. package/server/ceo/template-store.ts +134 -0
  47. package/server/ceo/time-tracking-store.ts +227 -0
  48. package/server/claude-auth-monitor.ts +128 -0
  49. package/server/claude-code-worker.ts +86 -0
  50. package/server/claude-session-discovery.ts +74 -1
  51. package/server/cli-launcher.ts +32 -10
  52. package/server/codex-adapter.ts +2 -2
  53. package/server/codex-ws-proxy.cjs +1 -1
  54. package/server/container-manager.ts +4 -4
  55. package/server/content-intelligence/content-engine.ts +1112 -0
  56. package/server/content-intelligence/platform-knowledge.ts +870 -0
  57. package/server/cron-store.ts +3 -3
  58. package/server/embedding-service.ts +49 -0
  59. package/server/event-bus-types.ts +13 -0
  60. package/server/federation/node-store.ts +5 -4
  61. package/server/fs-utils.ts +28 -1
  62. package/server/hank-notifications-store.ts +91 -0
  63. package/server/hank-tool-executor.ts +1835 -0
  64. package/server/hank-tools.ts +2107 -0
  65. package/server/image-pull-manager.ts +2 -2
  66. package/server/index.ts +25 -2
  67. package/server/llm-providers-streaming.ts +541 -0
  68. package/server/llm-providers.ts +12 -0
  69. package/server/marketplace.ts +249 -0
  70. package/server/mcp-registry.ts +158 -0
  71. package/server/memory-service.ts +296 -0
  72. package/server/obsidian-sync.ts +184 -0
  73. package/server/provider-manager.ts +5 -2
  74. package/server/provider-registry.ts +12 -0
  75. package/server/reminder-scheduler.ts +37 -1
  76. package/server/routes/agent-routes.ts +2 -1
  77. package/server/routes/assistant-routes.ts +198 -5
  78. package/server/routes/ceo-finance-kpi-routes.ts +167 -0
  79. package/server/routes/ceo-news-time-routes.ts +137 -0
  80. package/server/routes/ceo-routes.ts +99 -0
  81. package/server/routes/content-routes.ts +116 -0
  82. package/server/routes/email-routes.ts +147 -0
  83. package/server/routes/env-routes.ts +3 -3
  84. package/server/routes/fs-routes.ts +12 -9
  85. package/server/routes/hank-chat-routes.ts +592 -0
  86. package/server/routes/llm-routes.ts +12 -0
  87. package/server/routes/marketplace-routes.ts +63 -0
  88. package/server/routes/media-routes.ts +1 -1
  89. package/server/routes/memory-routes.ts +127 -0
  90. package/server/routes/platform-routes.ts +14 -675
  91. package/server/routes/sandbox-routes.ts +1 -1
  92. package/server/routes/settings-routes.ts +51 -1
  93. package/server/routes/socialmedia-routes.ts +152 -2
  94. package/server/routes/system-routes.ts +2 -2
  95. package/server/routes/team-routes.ts +71 -0
  96. package/server/routes/telephony-routes.ts +98 -18
  97. package/server/routes.ts +36 -9
  98. package/server/session-creation-service.ts +2 -2
  99. package/server/session-orchestrator.ts +54 -2
  100. package/server/session-types.ts +2 -0
  101. package/server/settings-manager.ts +50 -2
  102. package/server/skill-discovery.ts +68 -0
  103. package/server/socialmedia/adapters/browser-adapter.ts +179 -0
  104. package/server/socialmedia/adapters/postiz-adapter.ts +291 -14
  105. package/server/socialmedia/manager.ts +234 -15
  106. package/server/socialmedia/store.ts +51 -1
  107. package/server/socialmedia/types.ts +35 -2
  108. package/server/socialview/browser-manager.ts +150 -0
  109. package/server/socialview/extractors.ts +1298 -0
  110. package/server/socialview/image-describe.ts +188 -0
  111. package/server/socialview/library.ts +119 -0
  112. package/server/socialview/poster.ts +276 -0
  113. package/server/socialview/routes.ts +371 -0
  114. package/server/socialview/style-analyzer.ts +187 -0
  115. package/server/socialview/style-profiles.ts +67 -0
  116. package/server/socialview/types.ts +166 -0
  117. package/server/socialview/vision.ts +127 -0
  118. package/server/socialview/vnc-manager.ts +110 -0
  119. package/server/style-injector.ts +135 -0
  120. package/server/team-service.ts +239 -0
  121. package/server/team-store.ts +75 -0
  122. package/server/team-types.ts +52 -0
  123. package/server/telephony/audio-bridge.ts +281 -35
  124. package/server/telephony/audio-recorder.ts +132 -0
  125. package/server/telephony/call-manager.ts +803 -104
  126. package/server/telephony/call-types.ts +67 -1
  127. package/server/telephony/esl-client.ts +319 -0
  128. package/server/telephony/freeswitch-sync.ts +155 -0
  129. package/server/telephony/phone-utils.ts +63 -0
  130. package/server/telephony/telephony-store.ts +9 -8
  131. package/server/url-validator.ts +82 -0
  132. package/server/vault-markdown.ts +317 -0
  133. package/server/vault-migration.ts +121 -0
  134. package/server/vault-store.ts +466 -0
  135. package/server/vault-watcher.ts +59 -0
  136. package/server/vector-store.ts +210 -0
  137. package/server/voice-pipeline/gemini-live-adapter.ts +97 -0
  138. package/server/voice-pipeline/greeting-cache.ts +200 -0
  139. package/server/voice-pipeline/manager.ts +249 -0
  140. package/server/voice-pipeline/pipeline.ts +335 -0
  141. package/server/voice-pipeline/providers/index.ts +47 -0
  142. package/server/voice-pipeline/providers/llm-internal.ts +527 -0
  143. package/server/voice-pipeline/providers/stt-google.ts +157 -0
  144. package/server/voice-pipeline/providers/tts-google.ts +126 -0
  145. package/server/voice-pipeline/types.ts +247 -0
  146. package/server/ws-bridge-types.ts +6 -1
  147. package/dist/assets/AssistantPage-DJ-cMQfb.js +0 -1
  148. package/dist/assets/HelpPage-DMfkzERp.js +0 -1
  149. package/dist/assets/MediaPage-CE5rdvkC.js +0 -1
  150. package/dist/assets/RunsPage-C5BZF5Rx.js +0 -1
  151. package/dist/assets/SettingsPage-DirhjQrJ.js +0 -51
  152. package/dist/assets/SocialMediaPage-DBuM28vD.js +0 -1
  153. package/dist/assets/TelephonyPage-x0VV0fOo.js +0 -1
  154. package/dist/assets/index-C8M_PUmX.css +0 -32
  155. package/dist/assets/sw-register-LSSpj6RU.js +0 -1
  156. package/server/socialmedia/adapters/ayrshare-adapter.ts +0 -169
@@ -0,0 +1,2107 @@
1
+ // ─── Hank Tools ─────────────────────────────────────────────────────────────
2
+ // Shared tool declarations + system prompt for all Hank-UI providers.
3
+ // Extracted from gemini-live-client.ts to be used server-side by all providers.
4
+
5
+ // ─── Interfaces ─────────────────────────────────────────────────────────────
6
+ export interface AgentInfo {
7
+ id: string;
8
+ name: string;
9
+ description: string;
10
+ backend: string;
11
+ }
12
+
13
+ export interface ConversationContext {
14
+ title: string;
15
+ content: string;
16
+ }
17
+
18
+ export interface ActiveSession {
19
+ sessionId: string;
20
+ state: string;
21
+ model?: string;
22
+ agentName?: string;
23
+ cwd?: string;
24
+ }
25
+
26
+ export interface PhoneContact {
27
+ name: string;
28
+ phone: string;
29
+ notes?: string;
30
+ }
31
+
32
+ // ─── System Prompt Builder ──────────────────────────────────────────────────
33
+ import { listProfiles as listStyleProfiles } from "./socialview/style-profiles.js";
34
+ import { listInstalledSkills } from "./skill-discovery.js";
35
+
36
+ export function buildSystemPrompt(assistantName: string, agents: AgentInfo[], recentConversations?: ConversationContext[], activeSessions?: ActiveSession[], userName?: string, contacts?: PhoneContact[], obsidianVaultPath?: string): string {
37
+ // Snapshot of installed Claude Code skills under ~/.claude/skills/. Hank
38
+ // can invoke any of these directly via the `run_skill` tool to handle
39
+ // multi-turn structured workflows (content plans, post writing, audits…)
40
+ // without delegating to a fire-and-forget agent run.
41
+ let skillsSection = "";
42
+ try {
43
+ const skills = listInstalledSkills().filter((s) => s.description);
44
+ if (skills.length > 0) {
45
+ const lines = skills.map((s) => "- " + s.slug + ": " + s.description).join("\n");
46
+ skillsSection = "\nINSTALLED SKILLS (invoke via `run_skill`):\n"
47
+ + "Skills are structured multi-stage workflows. PREFER calling `run_skill(slug)`\n"
48
+ + "over delegating to an agent when the user's request matches a skill's\n"
49
+ + "description — the skill produces a high-quality result and you can guide the\n"
50
+ + "user through it in this chat (multi-turn). After loading a skill, follow the\n"
51
+ + "skill's own instructions: ask any inputs it needs, run its stages, present\n"
52
+ + "its output. Use agents only for fire-and-forget async work that does not fit\n"
53
+ + "a skill.\n\n" + lines;
54
+ }
55
+ } catch {
56
+ // Skill discovery is best-effort.
57
+ }
58
+ // Snapshot of available style personas (loaded from ~/.heyhank/socialview/style-profiles).
59
+ // Hank only needs to know which handles exist so he can map a user-spoken name
60
+ // (e.g. "Rene Remsik") to the canonical handle for `generate_content`'s
61
+ // styleProfileHandle param. The actual style is consumed downstream by the
62
+ // content agent; Hank stays at the routing layer.
63
+ let personasSection = "";
64
+ try {
65
+ const profiles = listStyleProfiles();
66
+ if (profiles.length > 0) {
67
+ personasSection = `\nSTYLE PERSONAS (available for generate_content's \`styleProfileHandle\` param):
68
+ The following personas have been distilled from extracted social posts. When the user
69
+ asks for content "im Stil von X" / "wie X schreibt", map their name to the handle below
70
+ and pass it as \`styleProfileHandle\`. If unsure, ask. Never invent a handle that isn't listed.
71
+
72
+ ${profiles.map((p) => {
73
+ const name = p.displayName && p.displayName !== p.handle ? ` — ${p.displayName}` : "";
74
+ return `- ${p.handle} (${p.platform}${name})`;
75
+ }).join("\n")}`;
76
+ }
77
+ } catch {
78
+ // Reading personas is best-effort — never break Hank if storage is missing.
79
+ }
80
+
81
+ const nameIntro = assistantName
82
+ ? `You are "${assistantName}", a personal voice assistant on the HeyHank platform.`
83
+ : `You are a personal voice assistant on the HeyHank platform.`;
84
+ const userIntro = userName ? `\nThe user's name is ${userName}. Address them by name when appropriate.` : "";
85
+
86
+ const agentSection = agents.length > 0
87
+ ? `\nAGENTS (configured on the platform):
88
+ The following agents are available. You are the orchestrator — when the user assigns a task,
89
+ choose the appropriate agent and start it with run_agent (NOT create_session!).
90
+ run_agent uses the full agent profile with the correct model, prompt and permissions.
91
+
92
+ ${agents.map((a) => `- "${a.name}" (${a.backend}): ${a.description}`).join("\n")}
93
+
94
+ When the user mentions an agent name (even approximately, e.g. "Max 2.0" for "Agent Max 2.0"),
95
+ recognize it and start the appropriate session. Only ask if it is truly unclear.`
96
+ : "";
97
+
98
+ return `${nameIntro}${userIntro}
99
+ You speak English by default, unless the user speaks another language.
100
+ Keep your answers short and natural — you are a voice assistant, not a text bot.
101
+
102
+ You are the central assistant and orchestrator of the platform with the following capabilities:
103
+ ${agentSection}
104
+ ${skillsSection}
105
+
106
+ AGENT CONTROL:
107
+ - create_agent: Create a new specialized agent when no existing agent fits the task.
108
+ Provide a name, description, and detailed system prompt. You can optionally auto-start it.
109
+ Use this when the user asks for a task that requires a new agent type (e.g. "create an agent for data analysis").
110
+ - run_agent: Start an agent with a task (PREFERRED for all configured agents!)
111
+ The agent starts with its full profile (model, prompt, permissions, working directory).
112
+ Returns a session_id — REMEMBER this for monitoring!
113
+ Example: run_agent("Agent Max 2.0", "Create a website for...")
114
+ - monitor_agent_session: Check the status of a running agent
115
+ IMPORTANT: After every run_agent you MUST call monitor_agent_session to check progress!
116
+ Call it multiple times (every few seconds) until the agent is done or has questions.
117
+ If the agent has questions (needsInput=true) → immediately inform the user!
118
+ If the agent is done (isCompleted=true) → inform the user that the task is completed.
119
+ - list_sessions: show active sessions
120
+ - create_session: start a new blank session (only if no matching agent exists)
121
+ - send_message: send a message to a running session
122
+ - get_session_status: check the status of a session
123
+
124
+ TODOS (task list):
125
+ - list_todos: show open tasks (filterable by priority/category)
126
+ - add_todo: add new task
127
+ - complete_todo: mark task as completed
128
+ - update_todo: update a task
129
+ - delete_todo: delete a task
130
+ Categories e.g.: work, personal, shopping, project
131
+
132
+ DELEGATIONS (tasks assigned to others):
133
+ - delegate_task: create a task delegated to someone (e.g. "Ask Peter to review the contract")
134
+ - list_delegations: show all delegated tasks, optionally filtered by person
135
+ When the user says "Peter soll..." or "delegate to...", use delegate_task.
136
+ When they ask "what did I delegate?" or "what's Peter working on?", use list_delegations.
137
+
138
+ PROJECTS (group related todos):
139
+ - list_projects: show all projects with progress (total/done/open)
140
+ - create_project: create a new project with optional initial todos
141
+ When the user says "create a project for..." or "start project...", use create_project.
142
+ When they ask "how are my projects?" or "project overview", use list_projects.
143
+
144
+ NOTES (memory):
145
+ - search_notes: search notes ("what do you know about X?")
146
+ - add_note: save a note ("remember that...", "note that...")
147
+ - update_note / delete_note: manage notes
148
+
149
+ MEMORY (long-term user preferences — IMPORTANT):
150
+ - save_memory: Remember a fact about the user. ALWAYS call this proactively.
151
+ - search_memory: Find relevant memories about the user.
152
+ - list_memories: List all known user facts.
153
+ - delete_memory: Forget something about the user.
154
+ When the user says "Merke dir dass...", "Remember that...", save it as a memory.
155
+ When the user says "Vergiss dass...", "Forget that...", delete the relevant memory.
156
+ CRITICAL: You MUST call save_memory whenever the user reveals ANY personal fact, preference, or detail about themselves. Examples:
157
+ - Name, location, timezone, language preferences
158
+ - Dietary preferences, allergies, health info
159
+ - Work schedule, job role, company
160
+ - Technical preferences (languages, frameworks, tools, OS)
161
+ - Family members, pets, hobbies
162
+ - Recurring habits or routines
163
+ Do NOT ask "should I remember this?" — just save it silently alongside your response.
164
+ Do NOT save transient requests, questions, or conversational filler.
165
+
166
+ CONTACTS/CRM:
167
+ - list_contacts: list all contacts (optional search query)
168
+ - add_contact: add a new contact (name required, optional: company, email, phone, notes, tags)
169
+ - update_contact: update an existing contact by ID
170
+ - search_contacts: search contacts by name, company, email, phone or tags
171
+ - log_interaction: log an interaction with a contact (call, email, meeting, note)
172
+ Use contacts to track people, relationships and interaction history.
173
+
174
+ DECISION LOG:
175
+ - log_decision: record an important decision with context, alternatives and reasoning
176
+ - search_decisions: search past decisions
177
+ Use the decision log when the user makes or discusses important decisions ("we decided to...", "let's go with...").
178
+
179
+ REMINDERS:
180
+ - list_reminders: show pending reminders
181
+ - add_reminder: set a reminder ("remind me in 2 hours about X")
182
+ Current timezone: Europe/Vienna
183
+ - delete_reminder: delete a reminder
184
+
185
+ EMAIL:
186
+ - list_email_accounts: show configured email accounts
187
+ - list_emails: list emails of an account (optional: unread only)
188
+ - read_email: read an email (by UID). ALWAYS read the body out loud. Summarize if long.
189
+ - search_emails: search emails
190
+ - send_email: send a NEW email (needs: account, to, subject, body)
191
+ - reply_email: reply to an email (needs: account, uid of original email, body text)
192
+ - email_summary: unread emails across all accounts
193
+ IMPORTANT email rules:
194
+ - When reading emails, speak the content (from, subject, body) out loud.
195
+ - When replying, confirm with the user what to say, then call reply_email with the uid of the original email.
196
+ - When sending, ask for recipient, subject, and body if not provided.
197
+ - Always confirm before actually sending: "Shall I send this?"
198
+ - Use the first available email account if the user doesn't specify one.
199
+
200
+ CALENDAR:
201
+ - list_calendar_accounts: show configured calendar accounts
202
+ - list_events: list events of an account (default: next 7 days)
203
+ - create_event: create an event ("schedule a meeting", "appointment on Friday")
204
+ - search_events: search events by text
205
+ - delete_event: delete an event (by UID)
206
+ - calendar_summary: overview of upcoming events across all accounts
207
+
208
+ DAILY BRIEFING:
209
+ - get_daily_briefing: Get a full overview of the day: emails, calendar, todos, delegations, projects
210
+ When the user asks things like "what's my day look like?", "Tagesüberblick", "briefing", "was steht an?",
211
+ "morning update", "daily summary", use get_daily_briefing.
212
+
213
+ MEETING NOTES:
214
+ - create_meeting_notes: Create meeting notes with summary, participants, and action items
215
+ After a call or meeting ends, offer to create meeting notes. Extract key points and action items.
216
+ When the user says "save meeting notes", "note from the meeting", "Besprechungsnotiz", use create_meeting_notes.
217
+
218
+ Use the tools proactively. If the user says "I still need to do X", add it as a todo.
219
+ If they say "don't forget" or "remember that", save a note.
220
+ If they mention a time, set a reminder.
221
+ If they ask about emails, use email_summary for an overview or list_emails for details.
222
+ If they ask about events ("what's on today?", "do I have anything tomorrow?"), use calendar_summary or list_events.
223
+ If they want to create an event ("schedule a...", "meeting on..."), use create_event.
224
+ If the user assigns a complex task (create a website, write code, etc.), delegate to the appropriate agent.
225
+
226
+ TEAM COORDINATION:
227
+ - run_team: For complex tasks that need multiple agents working in parallel. A Team Coordinator will break the goal into tasks, assign agents (or create new ones), manage git worktrees for isolation, and merge results.
228
+ Use this when the user asks for something complex that would benefit from multiple agents: "build a landing page", "implement 5 features", "refactor the entire module".
229
+ After starting a team, use monitor_team periodically to check progress.
230
+ - monitor_team: Check team progress. Call this after run_team to track status.
231
+ IMPORTANT: After every run_team you MUST call monitor_team to check progress!
232
+
233
+ TELEPHONY (phone calls):
234
+ - make_call: Place a real phone call via SIP trunk. The AI conducts the conversation autonomously.
235
+ IMPORTANT: You can ONLY call saved contacts by name. You cannot call arbitrary phone numbers.
236
+ Example: "Call Mama" → make_call("Mama", "Say hi and ask how she's doing")
237
+ Example: "Call Restaurant Steirereck" → make_call("Restaurant Steirereck", "Reserve a table for 4 at 7pm Friday")
238
+ If the user provides a phone number that's not a saved contact, ask them to save it first in Settings → Telephony → Contacts.
239
+ You can optionally pass listen=true to let the user hear the call live through their speakers.
240
+ Example: "Call Mama and let me listen" → make_call("Mama", "...", listen=true)
241
+ By default the ad-hoc task takes priority. If the user explicitly says something like "run the saved script" / "nutze das gespeicherte Script", pass useSavedScript=true to inject the stored call script as primary objective.
242
+ - list_active_calls: Show current active phone calls
243
+ - end_active_call: Hang up an active call
244
+ After starting a call, inform the user about the status. The call runs autonomously — you don't need to monitor it.${contacts && contacts.length > 0 ? `
245
+
246
+ PHONE CONTACTS (you may call these by name):
247
+ ${contacts.map((c) => `- "${c.name}": ${c.phone}${c.notes ? ` (${c.notes})` : ""}`).join("\n")}` : ""}
248
+
249
+ SOCIAL MEDIA:
250
+ - prepare_social_post: Prepare a social media post as a DRAFT. The user can review and publish it from the Social Media page.
251
+ - create_social_post: Create and IMMEDIATELY publish a post (use only when user explicitly says to post now)
252
+ - list_social_posts: List recent social media posts (optional: filter by status)
253
+ - get_social_analytics: Get analytics/metrics for a social media profile
254
+ - reply_to_social_comment: Reply to a comment on a social media post
255
+ - publish_draft: Publish an existing draft post (changes status from draft to published)
256
+ - update_draft: Update a draft post's text, platforms, or schedule
257
+ - delete_draft: Delete a draft post permanently
258
+ - schedule_post: Schedule an existing draft for a specific time
259
+
260
+ IMAGE UPLOADS:
261
+ When the user uploads an image, you will:
262
+ 1. SEE the image (it's sent to you visually)
263
+ 2. Receive a text message like "[Image uploaded and available at: /api/media/file/upload_xxx.jpg]"
264
+ You can use this URL directly in prepare_social_post as mediaUrls or thumbnailUrl.
265
+
266
+ IMPORTANT — ALWAYS ASK BEFORE CREATING A POST:
267
+ When the user asks to create a social media post, ALWAYS ask:
268
+ "Soll ich den Post selbst erstellen oder einen Agent beauftragen?
269
+ Selbst: Ich erstelle den Draft sofort.
270
+ Agent: Ein Agent kann zusätzlich Bilder generieren, recherchieren und den Text für jede Plattform optimieren."
271
+ - If user says "selbst" / "du" / "mach du" → use prepare_social_post
272
+ - If user says "agent" / "beauftrage" / "ein agent" / "agent soll das machen" / "content agent" → You MUST call the run_agent function immediately. Call run_agent with agent="Content Agent" and task="<detailed task with ALL context>". This is a FUNCTION CALL, not a text response. Do NOT just say you will start an agent — actually invoke the run_agent tool. Do NOT ask further questions like "which agent?" — just call the function right away. Include ALL context from the conversation in the task description so the agent has everything it needs.
273
+
274
+ CRITICAL TOOL CALLING RULE: When an action requires a tool (like run_agent, prepare_social_post, save_memory, etc.), you MUST actually call the function — do NOT just describe what you would do in text. Text responses about tools are USELESS. The user needs you to EXECUTE the function call.
275
+
276
+ AFTER CREATING A DRAFT (prepare_social_post response received):
277
+ Always tell the user the draft is ready and ask the complete workflow:
278
+ 1. Read back the post text briefly
279
+ 2. Ask: "Auf welchen Plattformen soll ich posten?" (if not already specified)
280
+ 3. Ask: "Wann soll der Post veröffentlicht werden? Sofort, oder soll ich einen Termin setzen?"
281
+ 4. If user gives a time → update the draft with scheduledAt
282
+ 5. If user says "poste jetzt" → publish the draft immediately using create_social_post
283
+ 6. If user wants to review first → tell them "Der Draft ist in der Social Media Seite unter Drafts, du kannst ihn dort bearbeiten und veröffentlichen."
284
+
285
+ Agent task instructions (include these when delegating):
286
+ - TWO image models available:
287
+ 1. Nano Banana 2 (PREFERRED for social media) — model: gemini-3.1-flash-image-preview via Gemini API. Can render TEXT IN IMAGES reliably (94% accuracy). Use for text overlays, quote graphics, carousels.
288
+ 2. Imagen 4 — POST /api/media/generate-image {prompt, aspectRatio?} → {images: [{filename, path}]}. Best photorealism. Supported aspectRatios: 1:1, 9:16, 16:9, 4:3, 3:4 (NO 4:5!).
289
+ - Generate videos: POST /api/media/generate-video {prompt, aspectRatio?, durationSeconds?} → {operationName}
290
+ Veo 3.1: native audio, up to 60s+, 720p/1080p. Poll: GET /api/media/video-status/{operationName}
291
+ - Files served at: /api/media/file/{filename}
292
+ - Create draft: POST /api/socialmedia/posts {text, platforms, isDraft: true, createdBy: "agent", mediaUrls?, videoUrl?, thumbnailUrl?, title?, firstComment?}
293
+ - Media URLs in draft: /api/media/file/{filename}
294
+ - Platform image aspect ratios: Instagram/Facebook 3:4 (Portrait), LinkedIn 1:1 (Square), X/YouTube 16:9
295
+ - firstComment: Use for links (LinkedIn, Facebook penalize external links in post text) and CTAs (Instagram)
296
+ - IMPORTANT: Use python3 urllib for JSON payloads with special chars (not curl -d) to avoid JSON parse errors
297
+ - HASHTAG POOLS: Curated hashtag sets per business are stored at GET /api/socialmedia/hashtag-pools. The Content Agent automatically fetches these before creating posts. Pools contain popular/medium/niche/branded/blocked tiers.
298
+ - WORKFLOW: Generate image → create draft with mediaUrls → inform user
299
+ - CRITICAL: After run_agent returns with status "completed": The agent is ALREADY DONE. Do NOT repeat what you said before. Do NOT say "gestartet", "erstellt jetzt", "arbeitet noch", or describe the task again. The user already saw "Content Agent ist fertig!" — just confirm briefly:
300
+ GOOD: "Fertig! Die Drafts sind bereit zur Freigabe."
301
+ BAD: "Ich habe den Content Agent gestartet. Er erstellt jetzt die Posts und generiert Bilder."
302
+ Keep it SHORT — one sentence max. Do NOT describe what the agent did.
303
+ - If run_agent returns "still_running": Say "Der Agent arbeitet noch — du kannst den Fortschritt auf der Agents-Seite verfolgen."
304
+
305
+ DOCUMENTS & FILES:
306
+ - list_documents: List all documents, optionally filtered by folder or tag.
307
+ - upload_document: Upload/create a new document (title, content, fileType, folder, tags).
308
+ - get_document: Retrieve a document's content and metadata by ID.
309
+ - search_documents: Full-text search across document titles, tags, folders, and summaries.
310
+ - delete_document: Delete a document.
311
+ Use these when the user says "save this document", "find the contract", "list my files", etc.
312
+
313
+ TEMPLATES:
314
+ - list_templates: List templates, optionally filtered by category (email, contract, meeting, invoice, report, custom).
315
+ - create_template: Create a reusable template with {{variable}} placeholders.
316
+ - use_template: Fill a template with variable values and return the result.
317
+ - search_templates: Search templates by name, category, or content.
318
+ - delete_template: Delete a template.
319
+ Use when the user says "create a template for...", "use the invoice template", "fill out the email template with...", etc.
320
+
321
+ NEWS & MONITORING:
322
+ - add_news_source: Add a new monitoring source (RSS feed, website, or keyword monitoring).
323
+ - list_news_sources: List all configured news sources.
324
+ - list_news: List latest news items, filterable by category, unread only, or saved only.
325
+ - search_news: Search news by keyword.
326
+ - mark_news_read: Mark a news item as read.
327
+ - get_news_stats: Get overview stats (total, unread, by category).
328
+ Use when the user says "monitor competitor X", "add RSS feed", "what's new?", "show me news about...", etc.
329
+
330
+ TIME TRACKING:
331
+ - start_timer: Start a timer for a task (with optional project/category).
332
+ - stop_timer: Stop the active timer.
333
+ - log_time: Manually log time (task, duration in minutes, project, notes).
334
+ - get_time_report: Get a time report for a period (today, week, month).
335
+ - get_active_timer: Check if a timer is currently running.
336
+ Use when the user says "start timer for...", "I worked 2 hours on...", "how much time this week?", "stop timer", etc.
337
+
338
+ FINANCE & INVOICES:
339
+ - create_invoice: Create a new invoice (client, items with description/quantity/unitPrice/total).
340
+ - list_invoices: List invoices, filtered by status (draft, sent, paid, overdue).
341
+ - mark_invoice_paid: Mark an invoice as paid.
342
+ - log_expense: Log a business expense (description, amount, category).
343
+ - list_expenses: List expenses, filtered by category or date range.
344
+ - get_financial_summary: Get financial overview (revenue, expenses, profit) for a period.
345
+ Use when the user says "create invoice for...", "log expense...", "how much revenue this month?", "outstanding invoices?", etc.
346
+
347
+ KPI DASHBOARD:
348
+ - define_kpi: Define a new KPI metric (name, unit, category, target, direction up/down).
349
+ - record_kpi_value: Record a new value for a KPI.
350
+ - get_kpi_dashboard: Get the full KPI dashboard with all metrics and status.
351
+ - get_kpi_history: Get historical values for a KPI over a period.
352
+ - delete_kpi: Delete a KPI metric.
353
+ Use when the user says "define a KPI for...", "update my revenue KPI", "show KPI dashboard", "how are my metrics?", etc.
354
+
355
+ CONTENT ENGINE (website analysis & content generation):
356
+ These tools require a WEBSITE URL — only use them when the user provides or
357
+ clearly references a specific website / company / product to analyze. For a
358
+ generic content plan around a niche or topic (no specific website), prefer
359
+ the content-30day-plan skill via run_skill instead.
360
+
361
+ - analyze_website: Crawl a website and extract brand identity, business type, products/services, colors, images, and tone of voice. Use when user says "analyze this website", "check out this URL", etc.
362
+ - create_content_strategy: Create a content marketing strategy based on website analysis. Includes content pillars, pain points, posting schedules, and customer journey mapping.
363
+ - generate_content: Generate platform-optimized content pieces with hooks, copywriting frameworks (PAS, AIDA, BAB, StoryBrand), hashtags, and image prompts.
364
+ - generate_ad_creatives: Generate ad creatives with copy, image prompts, and brand-aligned design specs for a specific platform.
365
+ - generate_ads: FULL AD WORKFLOW — Analyzes website, generates ad copy, creates images, and saves everything as Social Media drafts. One call does everything. Use this when the user wants actual ad drafts with images (not just copy).
366
+ - generate_content_plan: Generate a complete multi-week content plan across platforms. Combines strategy + content + ads into one comprehensive plan.
367
+ Use these tools when the user wants to:
368
+ - Analyze a website or competitor
369
+ - Create a content strategy from a specific website
370
+ - Generate social media content based on a business website
371
+ - Create ad campaigns
372
+ - Plan content calendars FROM A SPECIFIC WEBSITE
373
+ Do NOT ask for a URL when the user just wants ideas/posts for a topic or niche
374
+ without referencing a specific website — use a skill instead.
375
+
376
+ ${personasSection}
377
+
378
+ INTERNET SEARCH:
379
+ You have access to Google Search (automatically integrated). When the user asks for current information
380
+ ("what is...", "search for...", "what's the weather", "current news", etc.), use Google Search.
381
+ You can use it to retrieve current information from the internet.
382
+
383
+ IMPORTANT — AGENT MONITORING:
384
+ After starting an agent with run_agent:
385
+ 1. Remember the session_id from the response
386
+ 2. Immediately call monitor_agent_session to check the status
387
+ 3. If the agent is still working (isWorking=true), wait briefly and check again
388
+ 4. If the agent has questions (needsInput=true), inform the user IMMEDIATELY
389
+ 5. If the agent is done (isCompleted=true), let the user know
390
+ You are responsible for communicating progress to the user!${activeSessions && activeSessions.length > 0 ? `
391
+
392
+ ACTIVE SESSIONS (currently running):
393
+ ${activeSessions.map((s) => `- ${s.agentName || s.sessionId}: Status=${s.state}${s.model ? `, Model=${s.model}` : ""}${s.cwd ? `, Dir=${s.cwd}` : ""}`).join("\n")}
394
+ You can monitor these sessions with monitor_agent_session or get_session_status.
395
+ Proactively inform the user about running agents when they ask "what's running right now?".` : ""}${recentConversations && recentConversations.length > 0 ? `
396
+
397
+ CONVERSATION MEMORY:
398
+ Here are the recent conversations for context:
399
+ ${recentConversations.map((c) => `--- ${c.title} ---\n${c.content}`).join("\n\n")}
400
+ Use this knowledge to maintain context.` : ""}${obsidianVaultPath ? `
401
+
402
+ OBSIDIAN VAULT (Knowledge Base):
403
+ All your data is stored as Markdown files in the Obsidian vault at: ${obsidianVaultPath}/HeyHank/
404
+ - Memory/ — Long-term facts about the user (auto-synced with your memory tools)
405
+ - Notes/ — User's notes (synced with add_note/search_notes)
406
+ - Todos/ — Task list (synced with add_todo/list_todos)
407
+ - Reminders/ — Reminders (synced with add_reminder/list_reminders)
408
+ - Conversations/ — Past chat conversations (auto-saved)
409
+ - Calls/ — Phone call transcripts (auto-saved)
410
+ When you save a memory, note, or todo, it is automatically stored as a .md file in the vault.
411
+ The user can also edit these files directly in Obsidian — changes sync back automatically.
412
+ If the user asks "where is this saved?" or "where can I find my notes?", tell them about the vault location.` : ""}`;
413
+ }
414
+
415
+ // ─── Gemini Tool Declarations ───────────────────────────────────────────────
416
+ const TOOL_DECLARATIONS = [{
417
+ functionDeclarations: [
418
+ {
419
+ name: "run_skill",
420
+ description: "Load a Claude Code skill from ~/.claude/skills/<slug>/SKILL.md and follow its instructions in the current chat. Use this for structured multi-stage workflows (content plans, post writing, audits, code review etc.) — the skill output is returned to you, and you continue the workflow with the user (multi-turn). PREFER this over run_agent when the request matches a skill's description and benefits from interactive back-and-forth.",
421
+ parameters: {
422
+ type: "OBJECT",
423
+ properties: {
424
+ slug: {
425
+ type: "STRING",
426
+ description: "Skill slug (the directory name under ~/.claude/skills/, e.g. 'content-30day-plan').",
427
+ },
428
+ input: {
429
+ type: "STRING",
430
+ description: "Optional initial input/context the skill should start with (e.g. niche, topic, file path).",
431
+ },
432
+ },
433
+ required: ["slug"],
434
+ },
435
+ },
436
+ {
437
+ name: "run_agent",
438
+ description: "Run a configured agent by name or ID. This starts the agent with its full configuration (system prompt, model, permissions, working directory). Use this when the user asks to activate/start a specific agent like 'Max 2.0' or 'Coding Agent'. The agent will execute the given task autonomously.",
439
+ parameters: {
440
+ type: "OBJECT",
441
+ properties: {
442
+ agent: {
443
+ type: "STRING",
444
+ description: "Agent name or ID (e.g. 'Agent Max 2.0', 'Coding Agent', 'agent-max-20'). Fuzzy matching is supported.",
445
+ },
446
+ task: {
447
+ type: "STRING",
448
+ description: "The task/instruction to give to the agent.",
449
+ },
450
+ },
451
+ required: ["agent", "task"],
452
+ },
453
+ },
454
+ {
455
+ name: "create_agent",
456
+ description: "Create a new agent on the platform. Use this when the user wants a new specialized agent that doesn't exist yet. The agent will be saved and can be started later with run_agent.",
457
+ parameters: {
458
+ type: "OBJECT",
459
+ properties: {
460
+ name: {
461
+ type: "STRING",
462
+ description: "Name for the agent (e.g. 'Website Builder', 'Data Analyst')",
463
+ },
464
+ description: {
465
+ type: "STRING",
466
+ description: "Short description of what the agent does",
467
+ },
468
+ prompt: {
469
+ type: "STRING",
470
+ description: "System prompt / instructions for the agent. Be detailed about the agent's role, capabilities, and how it should approach tasks.",
471
+ },
472
+ model: {
473
+ type: "STRING",
474
+ description: "AI model to use. Default: 'claude-sonnet-4-20250514'. Options: 'claude-sonnet-4-20250514', 'claude-opus-4-20250514'",
475
+ },
476
+ cwd: {
477
+ type: "STRING",
478
+ description: "Working directory for the agent. Default: home directory.",
479
+ },
480
+ autoStart: {
481
+ type: "BOOLEAN",
482
+ description: "If true, immediately start the agent with a task after creation. Default: false.",
483
+ },
484
+ task: {
485
+ type: "STRING",
486
+ description: "Task to give the agent if autoStart is true.",
487
+ },
488
+ },
489
+ required: ["name", "prompt"],
490
+ },
491
+ },
492
+ {
493
+ name: "list_sessions",
494
+ description: "List all active coding sessions on the platform. Returns session IDs, status, model, and working directory.",
495
+ parameters: {
496
+ type: "OBJECT",
497
+ properties: {},
498
+ },
499
+ },
500
+ {
501
+ name: "create_session",
502
+ description: "Create a new coding session with Claude Code or Codex. Returns the new session ID.",
503
+ parameters: {
504
+ type: "OBJECT",
505
+ properties: {
506
+ backend: {
507
+ type: "STRING",
508
+ description: "The AI backend to use: 'claude' for Claude Code, 'codex' for OpenAI Codex. Default: 'claude'.",
509
+ enum: ["claude", "codex"],
510
+ },
511
+ cwd: {
512
+ type: "STRING",
513
+ description: "Working directory for the session. Default: /opt/agentplatform/web",
514
+ },
515
+ message: {
516
+ type: "STRING",
517
+ description: "Optional initial message to send to the session after creation.",
518
+ },
519
+ },
520
+ },
521
+ },
522
+ {
523
+ name: "send_message",
524
+ description: "Send a text message/instruction to an existing coding session. The AI in that session will execute it.",
525
+ parameters: {
526
+ type: "OBJECT",
527
+ properties: {
528
+ session_id: {
529
+ type: "STRING",
530
+ description: "The session ID to send the message to. Use list_sessions to find available sessions.",
531
+ },
532
+ message: {
533
+ type: "STRING",
534
+ description: "The message/instruction to send to the session.",
535
+ },
536
+ },
537
+ required: ["session_id", "message"],
538
+ },
539
+ },
540
+ {
541
+ name: "get_session_status",
542
+ description: "Get detailed status of a specific session including state, model, and recent activity.",
543
+ parameters: {
544
+ type: "OBJECT",
545
+ properties: {
546
+ session_id: {
547
+ type: "STRING",
548
+ description: "The session ID to check.",
549
+ },
550
+ },
551
+ required: ["session_id"],
552
+ },
553
+ },
554
+ {
555
+ name: "monitor_agent_session",
556
+ description: "Monitor a running agent session. Returns whether the agent needs user input (permission questions), is still working, or has completed. IMPORTANT: After starting an agent with run_agent, use this tool periodically to check progress and immediately inform the user about questions or completion.",
557
+ parameters: {
558
+ type: "OBJECT",
559
+ properties: {
560
+ session_id: {
561
+ type: "STRING",
562
+ description: "The session ID returned by run_agent.",
563
+ },
564
+ },
565
+ required: ["session_id"],
566
+ },
567
+ },
568
+ // ─── Todo Tools ─────────────────────────────────────────────────
569
+ {
570
+ name: "list_todos",
571
+ description: "List all todos/tasks. Can filter by status, priority or category.",
572
+ parameters: {
573
+ type: "OBJECT",
574
+ properties: {
575
+ show_completed: {
576
+ type: "BOOLEAN",
577
+ description: "If true, also show completed todos. Default: false (only open todos).",
578
+ },
579
+ priority: {
580
+ type: "STRING",
581
+ description: "Filter by priority.",
582
+ enum: ["high", "medium", "low"],
583
+ },
584
+ category: {
585
+ type: "STRING",
586
+ description: "Filter by category (e.g. 'arbeit', 'privat', 'projekt').",
587
+ },
588
+ },
589
+ },
590
+ },
591
+ {
592
+ name: "add_todo",
593
+ description: "Add a new todo/task to the list.",
594
+ parameters: {
595
+ type: "OBJECT",
596
+ properties: {
597
+ text: {
598
+ type: "STRING",
599
+ description: "The todo text/description.",
600
+ },
601
+ priority: {
602
+ type: "STRING",
603
+ description: "Priority level. Default: 'medium'.",
604
+ enum: ["high", "medium", "low"],
605
+ },
606
+ category: {
607
+ type: "STRING",
608
+ description: "Optional category (e.g. 'arbeit', 'privat', 'projekt').",
609
+ },
610
+ },
611
+ required: ["text"],
612
+ },
613
+ },
614
+ {
615
+ name: "complete_todo",
616
+ description: "Mark a todo as completed.",
617
+ parameters: {
618
+ type: "OBJECT",
619
+ properties: {
620
+ id: { type: "STRING", description: "The todo ID to complete." },
621
+ },
622
+ required: ["id"],
623
+ },
624
+ },
625
+ {
626
+ name: "delete_todo",
627
+ description: "Delete a todo permanently.",
628
+ parameters: {
629
+ type: "OBJECT",
630
+ properties: {
631
+ id: { type: "STRING", description: "The todo ID to delete." },
632
+ },
633
+ required: ["id"],
634
+ },
635
+ },
636
+ {
637
+ name: "update_todo",
638
+ description: "Update an existing todo's text, priority or category.",
639
+ parameters: {
640
+ type: "OBJECT",
641
+ properties: {
642
+ id: { type: "STRING", description: "The todo ID to update." },
643
+ text: { type: "STRING", description: "New text." },
644
+ priority: { type: "STRING", enum: ["high", "medium", "low"] },
645
+ category: { type: "STRING", description: "New category." },
646
+ },
647
+ required: ["id"],
648
+ },
649
+ },
650
+ // ─── Delegation Tools ────────────────────────────────────────────
651
+ {
652
+ name: "delegate_task",
653
+ description: "Create a task delegated to another person. Use when the user says 'delegate to...', 'ask X to do...' or 'X soll...'.",
654
+ parameters: {
655
+ type: "OBJECT",
656
+ properties: {
657
+ text: { type: "STRING", description: "The task description." },
658
+ delegatedTo: { type: "STRING", description: "Name of the person to delegate to." },
659
+ dueDate: { type: "STRING", description: "Due date in ISO format YYYY-MM-DD." },
660
+ priority: { type: "STRING", description: "Priority level.", enum: ["high", "medium", "low"] },
661
+ category: { type: "STRING", description: "Optional category." },
662
+ project: { type: "STRING", description: "Optional project name." },
663
+ },
664
+ required: ["text", "delegatedTo"],
665
+ },
666
+ },
667
+ {
668
+ name: "list_delegations",
669
+ description: "List all tasks delegated to others. Optionally filter by person name.",
670
+ parameters: {
671
+ type: "OBJECT",
672
+ properties: {
673
+ person: { type: "STRING", description: "Filter by person name (optional)." },
674
+ },
675
+ },
676
+ },
677
+ // ─── Project Tools ──────────────────────────────────────────────
678
+ {
679
+ name: "list_projects",
680
+ description: "List all projects with progress. Groups todos by project field and shows total/done/open count.",
681
+ parameters: {
682
+ type: "OBJECT",
683
+ properties: {},
684
+ },
685
+ },
686
+ {
687
+ name: "create_project",
688
+ description: "Create a new project. Creates a project note and optionally initial todos assigned to the project.",
689
+ parameters: {
690
+ type: "OBJECT",
691
+ properties: {
692
+ name: { type: "STRING", description: "Project name." },
693
+ description: { type: "STRING", description: "Project description." },
694
+ todos: {
695
+ type: "ARRAY",
696
+ items: { type: "STRING" },
697
+ description: "Optional list of initial todo texts for the project.",
698
+ },
699
+ },
700
+ required: ["name"],
701
+ },
702
+ },
703
+ // ─── Note Tools ──────────────────────────────────────────────────
704
+ {
705
+ name: "search_notes",
706
+ description: "Search notes/memory. Returns all notes if no query given. Use this when the user asks 'what do you know about X' or 'did I note something about Y'.",
707
+ parameters: {
708
+ type: "OBJECT",
709
+ properties: {
710
+ query: {
711
+ type: "STRING",
712
+ description: "Search term to filter notes by title, content or tags. Leave empty to list all.",
713
+ },
714
+ },
715
+ },
716
+ },
717
+ {
718
+ name: "add_note",
719
+ description: "Save a note/memory. Use when user says 'remember that...', 'note that...', 'save that...'.",
720
+ parameters: {
721
+ type: "OBJECT",
722
+ properties: {
723
+ title: { type: "STRING", description: "Short title for the note." },
724
+ content: { type: "STRING", description: "Detailed content." },
725
+ tags: { type: "STRING", description: "Comma-separated tags for categorization." },
726
+ },
727
+ required: ["title"],
728
+ },
729
+ },
730
+ {
731
+ name: "update_note",
732
+ description: "Update an existing note.",
733
+ parameters: {
734
+ type: "OBJECT",
735
+ properties: {
736
+ id: { type: "STRING", description: "The note ID." },
737
+ title: { type: "STRING" },
738
+ content: { type: "STRING" },
739
+ tags: { type: "STRING", description: "Comma-separated tags." },
740
+ },
741
+ required: ["id"],
742
+ },
743
+ },
744
+ {
745
+ name: "delete_note",
746
+ description: "Delete a note.",
747
+ parameters: {
748
+ type: "OBJECT",
749
+ properties: {
750
+ id: { type: "STRING", description: "The note ID to delete." },
751
+ },
752
+ required: ["id"],
753
+ },
754
+ },
755
+ // ─── Reminder Tools ──────────────────────────────────────────────
756
+ {
757
+ name: "list_reminders",
758
+ description: "List all pending reminders.",
759
+ parameters: {
760
+ type: "OBJECT",
761
+ properties: {
762
+ include_fired: {
763
+ type: "BOOLEAN",
764
+ description: "If true, also show already fired reminders.",
765
+ },
766
+ },
767
+ },
768
+ },
769
+ {
770
+ name: "add_reminder",
771
+ description: "Set a reminder for a specific time. Use when user says 'remind me in 2 hours' or 'remind me tomorrow at 9'.",
772
+ parameters: {
773
+ type: "OBJECT",
774
+ properties: {
775
+ text: { type: "STRING", description: "What to be reminded about." },
776
+ trigger_at: {
777
+ type: "STRING",
778
+ description: "ISO 8601 datetime when the reminder should fire. Calculate from current time if user says 'in 2 hours' etc. Current timezone is Europe/Vienna (CET/CEST).",
779
+ },
780
+ },
781
+ required: ["text", "trigger_at"],
782
+ },
783
+ },
784
+ {
785
+ name: "delete_reminder",
786
+ description: "Delete/cancel a reminder.",
787
+ parameters: {
788
+ type: "OBJECT",
789
+ properties: {
790
+ id: { type: "STRING", description: "The reminder ID to delete." },
791
+ },
792
+ required: ["id"],
793
+ },
794
+ },
795
+ // ─── Email Tools ──────────────────────────────────────────────────
796
+ {
797
+ name: "list_email_accounts",
798
+ description: "List all configured email accounts. Shows account name and email address.",
799
+ parameters: {
800
+ type: "OBJECT",
801
+ properties: {},
802
+ },
803
+ },
804
+ {
805
+ name: "list_emails",
806
+ description: "List recent emails from a specific account. Use list_email_accounts first to get the account name.",
807
+ parameters: {
808
+ type: "OBJECT",
809
+ properties: {
810
+ account: {
811
+ type: "STRING",
812
+ description: "Account name or email address (e.g. 'Gmail', 'Work', 'user@example.com').",
813
+ },
814
+ folder: {
815
+ type: "STRING",
816
+ description: "Mail folder. Default: INBOX.",
817
+ },
818
+ limit: {
819
+ type: "NUMBER",
820
+ description: "Number of emails to fetch. Default: 10.",
821
+ },
822
+ unseen: {
823
+ type: "BOOLEAN",
824
+ description: "If true, only show unread emails.",
825
+ },
826
+ },
827
+ required: ["account"],
828
+ },
829
+ },
830
+ {
831
+ name: "read_email",
832
+ description: "Read the full content of a specific email by UID. Use list_emails first to get the UID.",
833
+ parameters: {
834
+ type: "OBJECT",
835
+ properties: {
836
+ account: {
837
+ type: "STRING",
838
+ description: "Account name or email address.",
839
+ },
840
+ uid: {
841
+ type: "NUMBER",
842
+ description: "The email UID from list_emails.",
843
+ },
844
+ folder: {
845
+ type: "STRING",
846
+ description: "Mail folder. Default: INBOX.",
847
+ },
848
+ },
849
+ required: ["account", "uid"],
850
+ },
851
+ },
852
+ {
853
+ name: "search_emails",
854
+ description: "Search emails in an account by subject, sender, or body text.",
855
+ parameters: {
856
+ type: "OBJECT",
857
+ properties: {
858
+ account: {
859
+ type: "STRING",
860
+ description: "Account name or email address.",
861
+ },
862
+ query: {
863
+ type: "STRING",
864
+ description: "Search term to find in subject, from, or body.",
865
+ },
866
+ limit: {
867
+ type: "NUMBER",
868
+ description: "Max results. Default: 10.",
869
+ },
870
+ },
871
+ required: ["account", "query"],
872
+ },
873
+ },
874
+ {
875
+ name: "send_email",
876
+ description: "Send a new email from one of the configured accounts.",
877
+ parameters: {
878
+ type: "OBJECT",
879
+ properties: {
880
+ account: {
881
+ type: "STRING",
882
+ description: "Account name or email address to send from.",
883
+ },
884
+ to: {
885
+ type: "STRING",
886
+ description: "Recipient email address.",
887
+ },
888
+ subject: {
889
+ type: "STRING",
890
+ description: "Email subject line.",
891
+ },
892
+ body: {
893
+ type: "STRING",
894
+ description: "Email body text.",
895
+ },
896
+ },
897
+ required: ["account", "to", "subject", "body"],
898
+ },
899
+ },
900
+ {
901
+ name: "reply_email",
902
+ description: "Reply to an existing email. Automatically uses Re: subject and correct recipient.",
903
+ parameters: {
904
+ type: "OBJECT",
905
+ properties: {
906
+ account: {
907
+ type: "STRING",
908
+ description: "Account name or email address.",
909
+ },
910
+ uid: {
911
+ type: "NUMBER",
912
+ description: "UID of the email to reply to.",
913
+ },
914
+ body: {
915
+ type: "STRING",
916
+ description: "Reply body text.",
917
+ },
918
+ folder: {
919
+ type: "STRING",
920
+ description: "Mail folder of the original email. Default: INBOX.",
921
+ },
922
+ },
923
+ required: ["account", "uid", "body"],
924
+ },
925
+ },
926
+ {
927
+ name: "email_summary",
928
+ description: "Get unread email count across all configured accounts. Good for a quick overview.",
929
+ parameters: {
930
+ type: "OBJECT",
931
+ properties: {},
932
+ },
933
+ },
934
+ // ─── Calendar Tools ──────────────────────────────────────────────
935
+ {
936
+ name: "list_calendar_accounts",
937
+ description: "List all configured calendar accounts. Shows account name and provider.",
938
+ parameters: {
939
+ type: "OBJECT",
940
+ properties: {},
941
+ },
942
+ },
943
+ {
944
+ name: "list_events",
945
+ description: "List calendar events for a date range. Default: next 7 days. Use list_calendar_accounts first to get the account name.",
946
+ parameters: {
947
+ type: "OBJECT",
948
+ properties: {
949
+ account: {
950
+ type: "STRING",
951
+ description: "Calendar account name (e.g. 'Google', 'iCloud'). Use list_calendar_accounts to find available accounts.",
952
+ },
953
+ from: {
954
+ type: "STRING",
955
+ description: "Start date/time in ISO format (e.g. '2026-04-03'). Default: today.",
956
+ },
957
+ to: {
958
+ type: "STRING",
959
+ description: "End date/time in ISO format (e.g. '2026-04-10'). Default: 7 days from now.",
960
+ },
961
+ },
962
+ required: ["account"],
963
+ },
964
+ },
965
+ {
966
+ name: "create_event",
967
+ description: "Create a new calendar event. Use when the user says 'add to calendar', 'schedule a meeting', etc.",
968
+ parameters: {
969
+ type: "OBJECT",
970
+ properties: {
971
+ account: {
972
+ type: "STRING",
973
+ description: "Calendar account name.",
974
+ },
975
+ summary: {
976
+ type: "STRING",
977
+ description: "Event title/summary.",
978
+ },
979
+ start: {
980
+ type: "STRING",
981
+ description: "Start date/time in ISO format (e.g. '2026-04-05T14:00:00' or '2026-04-05' for all-day).",
982
+ },
983
+ end: {
984
+ type: "STRING",
985
+ description: "End date/time in ISO format (e.g. '2026-04-05T15:00:00' or '2026-04-06' for all-day).",
986
+ },
987
+ description: {
988
+ type: "STRING",
989
+ description: "Optional event description/notes.",
990
+ },
991
+ location: {
992
+ type: "STRING",
993
+ description: "Optional event location.",
994
+ },
995
+ all_day: {
996
+ type: "BOOLEAN",
997
+ description: "If true, create an all-day event. Default: false.",
998
+ },
999
+ },
1000
+ required: ["account", "summary", "start", "end"],
1001
+ },
1002
+ },
1003
+ {
1004
+ name: "search_events",
1005
+ description: "Search calendar events by text in title, description or location.",
1006
+ parameters: {
1007
+ type: "OBJECT",
1008
+ properties: {
1009
+ account: {
1010
+ type: "STRING",
1011
+ description: "Calendar account name.",
1012
+ },
1013
+ query: {
1014
+ type: "STRING",
1015
+ description: "Search text to find in event title, description or location.",
1016
+ },
1017
+ from: {
1018
+ type: "STRING",
1019
+ description: "Start of search range. Default: today.",
1020
+ },
1021
+ to: {
1022
+ type: "STRING",
1023
+ description: "End of search range. Default: 30 days from now.",
1024
+ },
1025
+ },
1026
+ required: ["account", "query"],
1027
+ },
1028
+ },
1029
+ {
1030
+ name: "delete_event",
1031
+ description: "Delete a calendar event by its UID. Use list_events or search_events first to find the UID.",
1032
+ parameters: {
1033
+ type: "OBJECT",
1034
+ properties: {
1035
+ account: {
1036
+ type: "STRING",
1037
+ description: "Calendar account name.",
1038
+ },
1039
+ uid: {
1040
+ type: "STRING",
1041
+ description: "The event UID to delete (from list_events).",
1042
+ },
1043
+ },
1044
+ required: ["account", "uid"],
1045
+ },
1046
+ },
1047
+ {
1048
+ name: "calendar_summary",
1049
+ description: "Get an overview of upcoming events across all configured calendar accounts. Shows today's count, this week's count, and next event.",
1050
+ parameters: {
1051
+ type: "OBJECT",
1052
+ properties: {},
1053
+ },
1054
+ },
1055
+ // ─── Telephony Tools ──────────────────────────────────────────────
1056
+ {
1057
+ name: "make_call",
1058
+ description: "Place a real phone call to a SAVED CONTACT. An AI assistant will conduct the conversation autonomously. You MUST use a contact name — arbitrary phone numbers are NOT allowed for safety.",
1059
+ parameters: {
1060
+ type: "OBJECT",
1061
+ properties: {
1062
+ phone: {
1063
+ type: "STRING",
1064
+ description: "Contact name (e.g. 'Mama', 'Restaurant Steirereck'). Must be a saved contact in Settings → Telephony → Contacts.",
1065
+ },
1066
+ task: {
1067
+ type: "STRING",
1068
+ description: "The task/instruction for the AI on the call. Be specific about what to say and achieve.",
1069
+ },
1070
+ voice: {
1071
+ type: "STRING",
1072
+ description: "Voice for the call AI. Default: same as current voice.",
1073
+ },
1074
+ listen: {
1075
+ type: "BOOLEAN",
1076
+ description: "If true, stream live call audio to the user's browser so they can listen in real-time.",
1077
+ },
1078
+ useSavedScript: {
1079
+ type: "BOOLEAN",
1080
+ description: "If true, the saved call-script for this contact (if any) is injected as PRIMARY OBJECTIVE. Default false — the ad-hoc task takes priority. Only set true when the user explicitly wants the saved script to run.",
1081
+ },
1082
+ },
1083
+ required: ["phone", "task"],
1084
+ },
1085
+ },
1086
+ {
1087
+ name: "list_active_calls",
1088
+ description: "List currently active phone calls with their status and duration.",
1089
+ parameters: {
1090
+ type: "OBJECT",
1091
+ properties: {},
1092
+ },
1093
+ },
1094
+ {
1095
+ name: "end_active_call",
1096
+ description: "Hang up an active phone call by its call ID.",
1097
+ parameters: {
1098
+ type: "OBJECT",
1099
+ properties: {
1100
+ call_id: {
1101
+ type: "STRING",
1102
+ description: "The call ID to hang up.",
1103
+ },
1104
+ },
1105
+ required: ["call_id"],
1106
+ },
1107
+ },
1108
+ // ─── Social Media ──────────────────────────────────────────────
1109
+ {
1110
+ name: "prepare_social_post",
1111
+ description: "Prepare a social media post as a draft for user review. The post will appear in the Social Media page where the user can edit, schedule, and publish it.",
1112
+ parameters: {
1113
+ type: "OBJECT",
1114
+ properties: {
1115
+ text: {
1116
+ type: "STRING",
1117
+ description: "The post text/content.",
1118
+ },
1119
+ platforms: {
1120
+ type: "ARRAY",
1121
+ items: { type: "STRING" },
1122
+ description: "Target platforms, e.g. [\"twitter\", \"linkedin\"].",
1123
+ },
1124
+ title: {
1125
+ type: "STRING",
1126
+ description: "Optional title/headline for the post.",
1127
+ },
1128
+ firstComment: {
1129
+ type: "STRING",
1130
+ description: "Optional text for the first comment (common on Instagram/LinkedIn).",
1131
+ },
1132
+ mediaUrls: {
1133
+ type: "ARRAY",
1134
+ items: { type: "STRING" },
1135
+ description: "Optional image URLs to attach.",
1136
+ },
1137
+ videoUrl: {
1138
+ type: "STRING",
1139
+ description: "Optional video URL.",
1140
+ },
1141
+ thumbnailUrl: {
1142
+ type: "STRING",
1143
+ description: "Optional thumbnail/preview image URL.",
1144
+ },
1145
+ scheduledAt: {
1146
+ type: "STRING",
1147
+ description: "Optional ISO 8601 datetime to schedule the post.",
1148
+ },
1149
+ },
1150
+ required: ["text", "platforms"],
1151
+ },
1152
+ },
1153
+ {
1154
+ name: "create_social_post",
1155
+ description: "Create and publish a social media post on one or more platforms.",
1156
+ parameters: {
1157
+ type: "OBJECT",
1158
+ properties: {
1159
+ text: {
1160
+ type: "STRING",
1161
+ description: "The post text/content.",
1162
+ },
1163
+ platforms: {
1164
+ type: "ARRAY",
1165
+ items: { type: "STRING" },
1166
+ description: "Platforms to post to, e.g. [\"twitter\", \"linkedin\"].",
1167
+ },
1168
+ scheduledAt: {
1169
+ type: "STRING",
1170
+ description: "Optional ISO 8601 datetime to schedule the post for later.",
1171
+ },
1172
+ },
1173
+ required: ["text", "platforms"],
1174
+ },
1175
+ },
1176
+ {
1177
+ name: "list_social_posts",
1178
+ description: "List recent social media posts. Optionally filter by status.",
1179
+ parameters: {
1180
+ type: "OBJECT",
1181
+ properties: {
1182
+ limit: {
1183
+ type: "INTEGER",
1184
+ description: "Max number of posts to return.",
1185
+ },
1186
+ status: {
1187
+ type: "STRING",
1188
+ description: "Filter by status: published, scheduled, failed.",
1189
+ },
1190
+ },
1191
+ },
1192
+ },
1193
+ {
1194
+ name: "get_social_analytics",
1195
+ description: "Get analytics/metrics for a social media profile.",
1196
+ parameters: {
1197
+ type: "OBJECT",
1198
+ properties: {
1199
+ profileId: {
1200
+ type: "STRING",
1201
+ description: "Profile/platform identifier.",
1202
+ },
1203
+ },
1204
+ required: ["profileId"],
1205
+ },
1206
+ },
1207
+ {
1208
+ name: "reply_to_social_comment",
1209
+ description: "Reply to a comment on a social media post.",
1210
+ parameters: {
1211
+ type: "OBJECT",
1212
+ properties: {
1213
+ postId: {
1214
+ type: "STRING",
1215
+ description: "The post ID.",
1216
+ },
1217
+ commentId: {
1218
+ type: "STRING",
1219
+ description: "The comment ID to reply to (optional for new comment).",
1220
+ },
1221
+ text: {
1222
+ type: "STRING",
1223
+ description: "Reply text.",
1224
+ },
1225
+ },
1226
+ required: ["postId", "text"],
1227
+ },
1228
+ },
1229
+ {
1230
+ name: "publish_draft",
1231
+ description: "Publish an existing draft post. Changes status from draft to published.",
1232
+ parameters: {
1233
+ type: "OBJECT",
1234
+ properties: {
1235
+ postId: {
1236
+ type: "STRING",
1237
+ description: "The draft post ID to publish.",
1238
+ },
1239
+ },
1240
+ required: ["postId"],
1241
+ },
1242
+ },
1243
+ {
1244
+ name: "update_draft",
1245
+ description: "Update an existing draft post's text, platforms, or schedule.",
1246
+ parameters: {
1247
+ type: "OBJECT",
1248
+ properties: {
1249
+ postId: {
1250
+ type: "STRING",
1251
+ description: "The draft post ID to update.",
1252
+ },
1253
+ text: {
1254
+ type: "STRING",
1255
+ description: "New post text.",
1256
+ },
1257
+ platforms: {
1258
+ type: "ARRAY",
1259
+ items: { type: "STRING" },
1260
+ description: "New target platforms.",
1261
+ },
1262
+ scheduledAt: {
1263
+ type: "STRING",
1264
+ description: "New schedule time (ISO 8601) or empty to unschedule.",
1265
+ },
1266
+ },
1267
+ required: ["postId"],
1268
+ },
1269
+ },
1270
+ {
1271
+ name: "delete_draft",
1272
+ description: "Delete a draft post permanently.",
1273
+ parameters: {
1274
+ type: "OBJECT",
1275
+ properties: {
1276
+ postId: {
1277
+ type: "STRING",
1278
+ description: "The draft post ID to delete.",
1279
+ },
1280
+ },
1281
+ required: ["postId"],
1282
+ },
1283
+ },
1284
+ {
1285
+ name: "schedule_post",
1286
+ description: "Schedule an existing draft post for a specific time.",
1287
+ parameters: {
1288
+ type: "OBJECT",
1289
+ properties: {
1290
+ postId: {
1291
+ type: "STRING",
1292
+ description: "The draft post ID to schedule.",
1293
+ },
1294
+ scheduledAt: {
1295
+ type: "STRING",
1296
+ description: "ISO 8601 datetime to publish the post.",
1297
+ },
1298
+ },
1299
+ required: ["postId", "scheduledAt"],
1300
+ },
1301
+ },
1302
+ // ─── Team Coordination Tools ────────────────────────────────────
1303
+ {
1304
+ name: "run_team",
1305
+ description: "Start a coordinated team of agents working in parallel on a complex goal. The Team Coordinator will break the goal into tasks, create isolated worktrees, assign agents, monitor progress, and merge results. Use this for multi-part tasks that benefit from parallel work by multiple agents.",
1306
+ parameters: {
1307
+ type: "OBJECT",
1308
+ properties: {
1309
+ goal: {
1310
+ type: "STRING",
1311
+ description: "The goal to achieve. Be specific.",
1312
+ },
1313
+ cwd: {
1314
+ type: "STRING",
1315
+ description: "Project directory (must be a git repo).",
1316
+ },
1317
+ agents: {
1318
+ type: "STRING",
1319
+ description: "Optional comma-separated agent names to use.",
1320
+ },
1321
+ },
1322
+ required: ["goal", "cwd"],
1323
+ },
1324
+ },
1325
+ {
1326
+ name: "monitor_team",
1327
+ description: "Check the status of a running team. Shows which agents are working, done, or failed.",
1328
+ parameters: {
1329
+ type: "OBJECT",
1330
+ properties: {
1331
+ team_id: {
1332
+ type: "STRING",
1333
+ description: "The team ID returned by run_team.",
1334
+ },
1335
+ },
1336
+ required: ["team_id"],
1337
+ },
1338
+ },
1339
+ {
1340
+ name: "fix_claude_auth",
1341
+ description: "Attempt to fix Claude Code authentication. Use when the user reports auth issues or when sessions fail with 401 errors.",
1342
+ parameters: {
1343
+ type: "OBJECT",
1344
+ properties: {},
1345
+ },
1346
+ },
1347
+ // ─── Memory Tools ──────────────────────────────────────────────────
1348
+ {
1349
+ name: "save_memory",
1350
+ description: "Save a fact, preference, or personal detail about the user. Use when the user shares personal information, preferences, or recurring facts. Examples: 'I'm vegetarian', 'My timezone is CET', 'I prefer dark mode'.",
1351
+ parameters: {
1352
+ type: "OBJECT",
1353
+ properties: {
1354
+ content: {
1355
+ type: "STRING",
1356
+ description: "The fact or preference to remember about the user.",
1357
+ },
1358
+ },
1359
+ required: ["content"],
1360
+ },
1361
+ },
1362
+ {
1363
+ name: "search_memory",
1364
+ description: "Search user memories by topic. Use to recall facts about the user relevant to the current conversation.",
1365
+ parameters: {
1366
+ type: "OBJECT",
1367
+ properties: {
1368
+ query: {
1369
+ type: "STRING",
1370
+ description: "Search query (e.g. 'food preferences', 'timezone', 'work schedule').",
1371
+ },
1372
+ },
1373
+ required: ["query"],
1374
+ },
1375
+ },
1376
+ {
1377
+ name: "list_memories",
1378
+ description: "List all saved user memories.",
1379
+ parameters: {
1380
+ type: "OBJECT",
1381
+ properties: {},
1382
+ },
1383
+ },
1384
+ {
1385
+ name: "delete_memory",
1386
+ description: "Delete a user memory. Use when the user says 'forget that...' or 'don't remember...'.",
1387
+ parameters: {
1388
+ type: "OBJECT",
1389
+ properties: {
1390
+ id: {
1391
+ type: "STRING",
1392
+ description: "The memory ID to delete. Use search_memory or list_memories first to find the ID.",
1393
+ },
1394
+ },
1395
+ required: ["id"],
1396
+ },
1397
+ },
1398
+ // ─── Contact Tools ──────────────────────────────────────────────────
1399
+ {
1400
+ name: "list_contacts",
1401
+ description: "List all contacts. Optionally search by name, company, email or tags.",
1402
+ parameters: {
1403
+ type: "OBJECT",
1404
+ properties: {
1405
+ search: {
1406
+ type: "STRING",
1407
+ description: "Optional search query to filter contacts.",
1408
+ },
1409
+ },
1410
+ },
1411
+ },
1412
+ {
1413
+ name: "add_contact",
1414
+ description: "Add a new contact to the CRM. Use when the user mentions a person they want to track.",
1415
+ parameters: {
1416
+ type: "OBJECT",
1417
+ properties: {
1418
+ name: { type: "STRING", description: "Contact name (required)." },
1419
+ company: { type: "STRING", description: "Company or organization." },
1420
+ email: { type: "STRING", description: "Email address." },
1421
+ phone: { type: "STRING", description: "Phone number." },
1422
+ notes: { type: "STRING", description: "Notes about the contact." },
1423
+ tags: { type: "STRING", description: "Comma-separated tags for categorization." },
1424
+ },
1425
+ required: ["name"],
1426
+ },
1427
+ },
1428
+ {
1429
+ name: "update_contact",
1430
+ description: "Update an existing contact's information.",
1431
+ parameters: {
1432
+ type: "OBJECT",
1433
+ properties: {
1434
+ id: { type: "STRING", description: "The contact ID to update." },
1435
+ name: { type: "STRING", description: "New name." },
1436
+ company: { type: "STRING", description: "New company." },
1437
+ email: { type: "STRING", description: "New email." },
1438
+ phone: { type: "STRING", description: "New phone." },
1439
+ notes: { type: "STRING", description: "New notes." },
1440
+ tags: { type: "STRING", description: "Comma-separated tags." },
1441
+ },
1442
+ required: ["id"],
1443
+ },
1444
+ },
1445
+ {
1446
+ name: "search_contacts",
1447
+ description: "Search contacts by name, company, email, phone or tags.",
1448
+ parameters: {
1449
+ type: "OBJECT",
1450
+ properties: {
1451
+ query: {
1452
+ type: "STRING",
1453
+ description: "Search query.",
1454
+ },
1455
+ },
1456
+ required: ["query"],
1457
+ },
1458
+ },
1459
+ {
1460
+ name: "log_interaction",
1461
+ description: "Log an interaction with a contact (call, email, meeting, note). Updates the contact's last contact date.",
1462
+ parameters: {
1463
+ type: "OBJECT",
1464
+ properties: {
1465
+ contactId: { type: "STRING", description: "The contact ID." },
1466
+ type: {
1467
+ type: "STRING",
1468
+ description: "Type of interaction.",
1469
+ enum: ["call", "email", "meeting", "note"],
1470
+ },
1471
+ summary: { type: "STRING", description: "Summary of the interaction." },
1472
+ },
1473
+ required: ["contactId", "type", "summary"],
1474
+ },
1475
+ },
1476
+ // ─── Decision Tools ─────────────────────────────────────────────────
1477
+ {
1478
+ name: "log_decision",
1479
+ description: "Record an important decision with context, alternatives considered, and reasoning. Use when the user makes or discusses a decision.",
1480
+ parameters: {
1481
+ type: "OBJECT",
1482
+ properties: {
1483
+ title: { type: "STRING", description: "Short title for the decision." },
1484
+ context: { type: "STRING", description: "Background context / what prompted this decision." },
1485
+ decision: { type: "STRING", description: "What was decided." },
1486
+ alternatives: { type: "STRING", description: "Comma-separated list of alternatives that were considered." },
1487
+ reasoning: { type: "STRING", description: "Why this option was chosen." },
1488
+ },
1489
+ required: ["title", "context", "decision"],
1490
+ },
1491
+ },
1492
+ {
1493
+ name: "search_decisions",
1494
+ description: "Search past decisions by title, context, decision text or reasoning.",
1495
+ parameters: {
1496
+ type: "OBJECT",
1497
+ properties: {
1498
+ query: {
1499
+ type: "STRING",
1500
+ description: "Search query.",
1501
+ },
1502
+ },
1503
+ required: ["query"],
1504
+ },
1505
+ },
1506
+ // ─── Daily Briefing ──────────────────────────────────────────────
1507
+ {
1508
+ name: "get_daily_briefing",
1509
+ description: "Get today's overview: emails, calendar, todos, delegations, projects. Use when the user asks for a daily briefing, morning update, or day overview.",
1510
+ parameters: {
1511
+ type: "OBJECT",
1512
+ properties: {
1513
+ date: {
1514
+ type: "STRING",
1515
+ description: "ISO date (YYYY-MM-DD) to get the briefing for. Defaults to today.",
1516
+ },
1517
+ },
1518
+ },
1519
+ },
1520
+ // ─── Meeting Notes ───────────────────────────────────────────────
1521
+ {
1522
+ name: "create_meeting_notes",
1523
+ description: "Create meeting notes with summary, participants, and action items. After a call or meeting ends, offer to create meeting notes.",
1524
+ parameters: {
1525
+ type: "OBJECT",
1526
+ properties: {
1527
+ title: {
1528
+ type: "STRING",
1529
+ description: "Meeting title.",
1530
+ },
1531
+ summary: {
1532
+ type: "STRING",
1533
+ description: "Summary of what was discussed.",
1534
+ },
1535
+ participants: {
1536
+ type: "STRING",
1537
+ description: "Comma-separated list of participant names.",
1538
+ },
1539
+ actionItems: {
1540
+ type: "STRING",
1541
+ description: "Comma-separated list of action items from the meeting.",
1542
+ },
1543
+ callId: {
1544
+ type: "STRING",
1545
+ description: "Optional telephony call ID to link to the meeting notes.",
1546
+ },
1547
+ },
1548
+ required: ["title", "summary"],
1549
+ },
1550
+ },
1551
+ // ─── Content Engine Tools ─────────────────────────────────────────
1552
+ {
1553
+ name: "analyze_website",
1554
+ description: "Analyze a website to extract brand identity, business type, products/services, colors, images, and tone of voice. Use when the user provides a URL and wants to understand a business or prepare content.",
1555
+ parameters: {
1556
+ type: "OBJECT",
1557
+ properties: {
1558
+ url: {
1559
+ type: "STRING",
1560
+ description: "The website URL to analyze (e.g. 'https://example.com').",
1561
+ },
1562
+ },
1563
+ required: ["url"],
1564
+ },
1565
+ },
1566
+ {
1567
+ name: "create_content_strategy",
1568
+ description: "Create a content marketing strategy based on website analysis. Includes content pillars, pain points, posting schedule, and journey mapping.",
1569
+ parameters: {
1570
+ type: "OBJECT",
1571
+ properties: {
1572
+ url: {
1573
+ type: "STRING",
1574
+ description: "The website URL to base the strategy on.",
1575
+ },
1576
+ platforms: {
1577
+ type: "STRING",
1578
+ description: "Comma-separated list of target platforms. Default: 'instagram,linkedin,facebook'. Options: instagram, linkedin, facebook, tiktok, x, youtube.",
1579
+ },
1580
+ },
1581
+ required: ["url"],
1582
+ },
1583
+ },
1584
+ {
1585
+ name: "generate_content",
1586
+ description: "Generate platform-optimized content pieces with hooks, copywriting frameworks, hashtags, and image prompts based on a website analysis.",
1587
+ parameters: {
1588
+ type: "OBJECT",
1589
+ properties: {
1590
+ url: {
1591
+ type: "STRING",
1592
+ description: "The website URL to generate content for.",
1593
+ },
1594
+ platform: {
1595
+ type: "STRING",
1596
+ description: "Target platform: instagram, linkedin, facebook, tiktok, x, youtube.",
1597
+ },
1598
+ count: {
1599
+ type: "NUMBER",
1600
+ description: "Number of content pieces to generate. Default: 5.",
1601
+ },
1602
+ journeyStage: {
1603
+ type: "STRING",
1604
+ description: "Customer journey stage: 'attract' (awareness), 'convert' (consideration), or 'close' (decision). Default: all stages.",
1605
+ },
1606
+ styleProfileHandle: {
1607
+ type: "STRING",
1608
+ description: "Optional handle of a SocialView role-model (e.g. 'rene.remsik') whose saved StyleProfile will drive the writing voice/structure. Use when the user asks for content 'im Stil von X' or 'wie X schreibt'. Profile must exist (created via SocialView style-profile analysis).",
1609
+ },
1610
+ },
1611
+ required: ["url", "platform"],
1612
+ },
1613
+ },
1614
+ {
1615
+ name: "generate_ad_creatives",
1616
+ description: "Generate ad creatives with copy, image prompts, and brand-aligned design specs for a specific platform.",
1617
+ parameters: {
1618
+ type: "OBJECT",
1619
+ properties: {
1620
+ url: {
1621
+ type: "STRING",
1622
+ description: "The website URL to generate ads for.",
1623
+ },
1624
+ platform: {
1625
+ type: "STRING",
1626
+ description: "Target ad platform: instagram, linkedin, facebook, tiktok, x, youtube.",
1627
+ },
1628
+ count: {
1629
+ type: "NUMBER",
1630
+ description: "Number of ad creatives to generate. Default: 3.",
1631
+ },
1632
+ },
1633
+ required: ["url", "platform"],
1634
+ },
1635
+ },
1636
+ {
1637
+ name: "generate_ads",
1638
+ description: "Full ad creation workflow: Analyzes a website, generates ad copy, creates images with AI, and saves everything as Social Media drafts ready for review. Returns draft IDs and a link to review them.",
1639
+ parameters: {
1640
+ type: "OBJECT",
1641
+ properties: {
1642
+ url: {
1643
+ type: "STRING",
1644
+ description: "The website URL to create ads for.",
1645
+ },
1646
+ platforms: {
1647
+ type: "STRING",
1648
+ description: "Comma-separated target platforms. Default: all connected profiles. Options: instagram, linkedin, facebook, twitter.",
1649
+ },
1650
+ count: {
1651
+ type: "NUMBER",
1652
+ description: "Number of ad variations per platform. Default: 2.",
1653
+ },
1654
+ style: {
1655
+ type: "STRING",
1656
+ description: "Visual style: 'professional' (clean, corporate), 'bold' (high contrast, attention-grabbing), 'minimal' (whitespace, elegant), 'playful' (colorful, casual). Default: 'professional'.",
1657
+ },
1658
+ },
1659
+ required: ["url"],
1660
+ },
1661
+ },
1662
+ {
1663
+ name: "generate_content_plan",
1664
+ description: "Generate a complete content plan for multiple weeks. Includes strategy, content pieces, and scheduling across platforms.",
1665
+ parameters: {
1666
+ type: "OBJECT",
1667
+ properties: {
1668
+ url: {
1669
+ type: "STRING",
1670
+ description: "The website URL to base the plan on.",
1671
+ },
1672
+ platforms: {
1673
+ type: "STRING",
1674
+ description: "Comma-separated platforms. Default: 'instagram,linkedin,facebook'.",
1675
+ },
1676
+ weeks: {
1677
+ type: "NUMBER",
1678
+ description: "Number of weeks to plan for. Default: 4.",
1679
+ },
1680
+ },
1681
+ required: ["url"],
1682
+ },
1683
+ },
1684
+ // ─── Documents ────────────────────────────────────────────────────
1685
+ {
1686
+ name: "list_documents",
1687
+ description: "List documents, optionally filtered by folder or tag.",
1688
+ parameters: {
1689
+ type: "OBJECT",
1690
+ properties: {
1691
+ folder: { type: "STRING", description: "Filter by folder name." },
1692
+ tag: { type: "STRING", description: "Filter by tag." },
1693
+ },
1694
+ },
1695
+ },
1696
+ {
1697
+ name: "upload_document",
1698
+ description: "Upload/create a new document.",
1699
+ parameters: {
1700
+ type: "OBJECT",
1701
+ properties: {
1702
+ title: { type: "STRING", description: "Document title." },
1703
+ content: { type: "STRING", description: "Document content." },
1704
+ fileType: { type: "STRING", description: "File type (txt, md, pdf, etc.)." },
1705
+ folder: { type: "STRING", description: "Folder to store in. Default: 'General'." },
1706
+ tags: { type: "STRING", description: "Comma-separated tags." },
1707
+ summary: { type: "STRING", description: "AI-generated summary of the document." },
1708
+ },
1709
+ required: ["title", "content", "fileType"],
1710
+ },
1711
+ },
1712
+ {
1713
+ name: "get_document",
1714
+ description: "Get a document's content and metadata by ID.",
1715
+ parameters: {
1716
+ type: "OBJECT",
1717
+ properties: {
1718
+ id: { type: "STRING", description: "Document ID." },
1719
+ },
1720
+ required: ["id"],
1721
+ },
1722
+ },
1723
+ {
1724
+ name: "search_documents",
1725
+ description: "Search documents by keyword across titles, tags, folders, and summaries.",
1726
+ parameters: {
1727
+ type: "OBJECT",
1728
+ properties: {
1729
+ query: { type: "STRING", description: "Search query." },
1730
+ },
1731
+ required: ["query"],
1732
+ },
1733
+ },
1734
+ {
1735
+ name: "delete_document",
1736
+ description: "Delete a document by ID.",
1737
+ parameters: {
1738
+ type: "OBJECT",
1739
+ properties: {
1740
+ id: { type: "STRING", description: "Document ID." },
1741
+ },
1742
+ required: ["id"],
1743
+ },
1744
+ },
1745
+ // ─── Templates ──────────────────────────────────────────────────
1746
+ {
1747
+ name: "list_templates",
1748
+ description: "List templates, optionally filtered by category.",
1749
+ parameters: {
1750
+ type: "OBJECT",
1751
+ properties: {
1752
+ category: { type: "STRING", description: "Filter by category (email, contract, meeting, invoice, report, custom)." },
1753
+ },
1754
+ },
1755
+ },
1756
+ {
1757
+ name: "create_template",
1758
+ description: "Create a reusable template with {{variable}} placeholders.",
1759
+ parameters: {
1760
+ type: "OBJECT",
1761
+ properties: {
1762
+ name: { type: "STRING", description: "Template name." },
1763
+ content: { type: "STRING", description: "Template content with {{variable}} placeholders." },
1764
+ category: { type: "STRING", description: "Category (email, contract, meeting, invoice, report, custom)." },
1765
+ tags: { type: "STRING", description: "Comma-separated tags." },
1766
+ },
1767
+ required: ["name", "content", "category"],
1768
+ },
1769
+ },
1770
+ {
1771
+ name: "use_template",
1772
+ description: "Fill a template with variable values and return the result.",
1773
+ parameters: {
1774
+ type: "OBJECT",
1775
+ properties: {
1776
+ id: { type: "STRING", description: "Template ID." },
1777
+ variables: { type: "STRING", description: "JSON object of variable name-value pairs, e.g. '{\"name\": \"John\", \"date\": \"2026-04-14\"}'." },
1778
+ },
1779
+ required: ["id", "variables"],
1780
+ },
1781
+ },
1782
+ {
1783
+ name: "search_templates",
1784
+ description: "Search templates by name, category, or content.",
1785
+ parameters: {
1786
+ type: "OBJECT",
1787
+ properties: {
1788
+ query: { type: "STRING", description: "Search query." },
1789
+ },
1790
+ required: ["query"],
1791
+ },
1792
+ },
1793
+ {
1794
+ name: "delete_template",
1795
+ description: "Delete a template by ID.",
1796
+ parameters: {
1797
+ type: "OBJECT",
1798
+ properties: {
1799
+ id: { type: "STRING", description: "Template ID." },
1800
+ },
1801
+ required: ["id"],
1802
+ },
1803
+ },
1804
+ // ─── News & Monitoring ──────────────────────────────────────────
1805
+ {
1806
+ name: "add_news_source",
1807
+ description: "Add a monitoring source (RSS feed, website, or keyword monitoring).",
1808
+ parameters: {
1809
+ type: "OBJECT",
1810
+ properties: {
1811
+ name: { type: "STRING", description: "Source name (e.g. 'TechCrunch', 'Competitor Blog')." },
1812
+ type: { type: "STRING", description: "Source type: rss, website, or keyword." },
1813
+ category: { type: "STRING", description: "Category (e.g. 'industry', 'competitor', 'technology')." },
1814
+ url: { type: "STRING", description: "URL for RSS or website type." },
1815
+ keywords: { type: "STRING", description: "Comma-separated keywords for keyword type." },
1816
+ checkInterval: { type: "NUMBER", description: "Check interval in minutes. Default: 60." },
1817
+ },
1818
+ required: ["name", "type", "category"],
1819
+ },
1820
+ },
1821
+ {
1822
+ name: "list_news_sources",
1823
+ description: "List all configured news monitoring sources.",
1824
+ parameters: { type: "OBJECT", properties: {} },
1825
+ },
1826
+ {
1827
+ name: "list_news",
1828
+ description: "List latest news items.",
1829
+ parameters: {
1830
+ type: "OBJECT",
1831
+ properties: {
1832
+ category: { type: "STRING", description: "Filter by category." },
1833
+ unreadOnly: { type: "BOOLEAN", description: "Show only unread items." },
1834
+ limit: { type: "NUMBER", description: "Max items to return. Default: 20." },
1835
+ },
1836
+ },
1837
+ },
1838
+ {
1839
+ name: "search_news",
1840
+ description: "Search news items by keyword.",
1841
+ parameters: {
1842
+ type: "OBJECT",
1843
+ properties: {
1844
+ query: { type: "STRING", description: "Search query." },
1845
+ },
1846
+ required: ["query"],
1847
+ },
1848
+ },
1849
+ {
1850
+ name: "mark_news_read",
1851
+ description: "Mark a news item as read.",
1852
+ parameters: {
1853
+ type: "OBJECT",
1854
+ properties: {
1855
+ id: { type: "STRING", description: "News item ID." },
1856
+ },
1857
+ required: ["id"],
1858
+ },
1859
+ },
1860
+ {
1861
+ name: "get_news_stats",
1862
+ description: "Get news overview: total items, unread count, by category.",
1863
+ parameters: { type: "OBJECT", properties: {} },
1864
+ },
1865
+ // ─── Time Tracking ──────────────────────────────────────────────
1866
+ {
1867
+ name: "start_timer",
1868
+ description: "Start a timer for a task.",
1869
+ parameters: {
1870
+ type: "OBJECT",
1871
+ properties: {
1872
+ task: { type: "STRING", description: "What you're working on." },
1873
+ project: { type: "STRING", description: "Project name." },
1874
+ category: { type: "STRING", description: "Category (e.g. development, meeting, admin)." },
1875
+ },
1876
+ required: ["task"],
1877
+ },
1878
+ },
1879
+ {
1880
+ name: "stop_timer",
1881
+ description: "Stop the currently running timer.",
1882
+ parameters: {
1883
+ type: "OBJECT",
1884
+ properties: {
1885
+ notes: { type: "STRING", description: "Optional notes about what was done." },
1886
+ },
1887
+ },
1888
+ },
1889
+ {
1890
+ name: "get_active_timer",
1891
+ description: "Check if a timer is currently running.",
1892
+ parameters: { type: "OBJECT", properties: {} },
1893
+ },
1894
+ {
1895
+ name: "log_time",
1896
+ description: "Manually log time spent on a task.",
1897
+ parameters: {
1898
+ type: "OBJECT",
1899
+ properties: {
1900
+ task: { type: "STRING", description: "What was worked on." },
1901
+ duration: { type: "NUMBER", description: "Duration in minutes." },
1902
+ project: { type: "STRING", description: "Project name." },
1903
+ category: { type: "STRING", description: "Category." },
1904
+ notes: { type: "STRING", description: "Notes." },
1905
+ date: { type: "STRING", description: "Date (ISO). Default: today." },
1906
+ },
1907
+ required: ["task", "duration"],
1908
+ },
1909
+ },
1910
+ {
1911
+ name: "get_time_report",
1912
+ description: "Get a time tracking report for a period.",
1913
+ parameters: {
1914
+ type: "OBJECT",
1915
+ properties: {
1916
+ period: { type: "STRING", description: "Period: today, week, month. Default: week." },
1917
+ },
1918
+ },
1919
+ },
1920
+ // ─── Finance & Invoices ─────────────────────────────────────────
1921
+ {
1922
+ name: "create_invoice",
1923
+ description: "Create a new invoice.",
1924
+ parameters: {
1925
+ type: "OBJECT",
1926
+ properties: {
1927
+ clientName: { type: "STRING", description: "Client/company name." },
1928
+ items: { type: "STRING", description: "JSON array of items, each with description, quantity, unitPrice, total. Example: '[{\"description\": \"Web Design\", \"quantity\": 1, \"unitPrice\": 2000, \"total\": 2000}]'" },
1929
+ clientEmail: { type: "STRING", description: "Client email." },
1930
+ taxRate: { type: "NUMBER", description: "Tax rate percentage. Default: from settings." },
1931
+ currency: { type: "STRING", description: "Currency code. Default: EUR." },
1932
+ dueDate: { type: "STRING", description: "Due date (YYYY-MM-DD). Default: 30 days." },
1933
+ notes: { type: "STRING", description: "Additional notes." },
1934
+ },
1935
+ required: ["clientName", "items"],
1936
+ },
1937
+ },
1938
+ {
1939
+ name: "list_invoices",
1940
+ description: "List invoices, filtered by status.",
1941
+ parameters: {
1942
+ type: "OBJECT",
1943
+ properties: {
1944
+ status: { type: "STRING", description: "Filter: draft, sent, paid, overdue, cancelled." },
1945
+ },
1946
+ },
1947
+ },
1948
+ {
1949
+ name: "mark_invoice_paid",
1950
+ description: "Mark an invoice as paid.",
1951
+ parameters: {
1952
+ type: "OBJECT",
1953
+ properties: {
1954
+ id: { type: "STRING", description: "Invoice ID." },
1955
+ },
1956
+ required: ["id"],
1957
+ },
1958
+ },
1959
+ {
1960
+ name: "log_expense",
1961
+ description: "Log a business expense.",
1962
+ parameters: {
1963
+ type: "OBJECT",
1964
+ properties: {
1965
+ description: { type: "STRING", description: "What was purchased/paid." },
1966
+ amount: { type: "NUMBER", description: "Amount." },
1967
+ category: { type: "STRING", description: "Category (e.g. software, office, travel, marketing)." },
1968
+ vendor: { type: "STRING", description: "Vendor/company name." },
1969
+ project: { type: "STRING", description: "Associated project." },
1970
+ date: { type: "STRING", description: "Date (YYYY-MM-DD). Default: today." },
1971
+ notes: { type: "STRING", description: "Additional notes." },
1972
+ },
1973
+ required: ["description", "amount", "category"],
1974
+ },
1975
+ },
1976
+ {
1977
+ name: "list_expenses",
1978
+ description: "List expenses, filtered by category or date range.",
1979
+ parameters: {
1980
+ type: "OBJECT",
1981
+ properties: {
1982
+ category: { type: "STRING", description: "Filter by category." },
1983
+ startDate: { type: "STRING", description: "Start date (YYYY-MM-DD)." },
1984
+ endDate: { type: "STRING", description: "End date (YYYY-MM-DD)." },
1985
+ },
1986
+ },
1987
+ },
1988
+ {
1989
+ name: "get_financial_summary",
1990
+ description: "Get financial overview: revenue, expenses, profit for a period.",
1991
+ parameters: {
1992
+ type: "OBJECT",
1993
+ properties: {
1994
+ period: { type: "STRING", description: "Period: month, quarter, year. Default: month." },
1995
+ },
1996
+ },
1997
+ },
1998
+ // ─── KPI Dashboard ──────────────────────────────────────────────
1999
+ {
2000
+ name: "define_kpi",
2001
+ description: "Define a new KPI metric to track.",
2002
+ parameters: {
2003
+ type: "OBJECT",
2004
+ properties: {
2005
+ name: { type: "STRING", description: "KPI name (e.g. 'Monthly Revenue', 'Customer Satisfaction')." },
2006
+ unit: { type: "STRING", description: "Unit (%, EUR, count, hours, etc.)." },
2007
+ category: { type: "STRING", description: "Category (revenue, growth, operations, marketing, custom)." },
2008
+ target: { type: "NUMBER", description: "Target value." },
2009
+ direction: { type: "STRING", description: "Direction: 'up' (higher=better) or 'down' (lower=better). Default: up." },
2010
+ description: { type: "STRING", description: "Description of this KPI." },
2011
+ },
2012
+ required: ["name", "unit", "category"],
2013
+ },
2014
+ },
2015
+ {
2016
+ name: "record_kpi_value",
2017
+ description: "Record a new value for a KPI.",
2018
+ parameters: {
2019
+ type: "OBJECT",
2020
+ properties: {
2021
+ kpiId: { type: "STRING", description: "KPI ID." },
2022
+ value: { type: "NUMBER", description: "The value to record." },
2023
+ date: { type: "STRING", description: "Date (YYYY-MM-DD). Default: today." },
2024
+ note: { type: "STRING", description: "Optional note." },
2025
+ },
2026
+ required: ["kpiId", "value"],
2027
+ },
2028
+ },
2029
+ {
2030
+ name: "get_kpi_dashboard",
2031
+ description: "Get the full KPI dashboard with all metrics, trends, and status.",
2032
+ parameters: { type: "OBJECT", properties: {} },
2033
+ },
2034
+ {
2035
+ name: "get_kpi_history",
2036
+ description: "Get historical values for a specific KPI.",
2037
+ parameters: {
2038
+ type: "OBJECT",
2039
+ properties: {
2040
+ kpiId: { type: "STRING", description: "KPI ID." },
2041
+ period: { type: "STRING", description: "Period: week, month, quarter, year." },
2042
+ },
2043
+ required: ["kpiId"],
2044
+ },
2045
+ },
2046
+ {
2047
+ name: "delete_kpi",
2048
+ description: "Delete a KPI metric.",
2049
+ parameters: {
2050
+ type: "OBJECT",
2051
+ properties: {
2052
+ id: { type: "STRING", description: "KPI ID." },
2053
+ },
2054
+ required: ["id"],
2055
+ },
2056
+ },
2057
+ ],
2058
+ }];
2059
+
2060
+ // ─── Gemini Format Export ───────────────────────────────────────────────────
2061
+ /** Returns tool declarations in Gemini format (functionDeclarations groups) */
2062
+ export function getToolDeclarationsGemini() {
2063
+ return TOOL_DECLARATIONS;
2064
+ }
2065
+
2066
+ // ─── OpenAI Format Converter ────────────────────────────────────────────────
2067
+
2068
+ /** Helper to convert Gemini schema types to OpenAI/JSON Schema types */
2069
+ function convertGeminiSchemaToOpenAI(schema: any): any {
2070
+ if (!schema) return { type: "object", properties: {} };
2071
+ const result: any = {};
2072
+ // Convert type: "OBJECT" → "object", "STRING" → "string", "NUMBER" → "number", "BOOLEAN" → "boolean", "INTEGER" → "integer", "ARRAY" → "array"
2073
+ result.type = (schema.type || "object").toLowerCase();
2074
+ if (schema.properties) {
2075
+ result.properties = {};
2076
+ for (const [key, val] of Object.entries(schema.properties)) {
2077
+ result.properties[key] = convertGeminiSchemaToOpenAI(val);
2078
+ }
2079
+ }
2080
+ if (schema.required) result.required = schema.required;
2081
+ if (schema.description) result.description = schema.description;
2082
+ if (schema.enum) result.enum = schema.enum;
2083
+ if (schema.items) result.items = convertGeminiSchemaToOpenAI(schema.items);
2084
+ return result;
2085
+ }
2086
+
2087
+ /** Returns tool declarations in OpenAI function-calling format */
2088
+ export function getToolDeclarationsOpenAI() {
2089
+ const geminiTools = getToolDeclarationsGemini();
2090
+ return geminiTools.flatMap(group =>
2091
+ group.functionDeclarations.map(fn => ({
2092
+ type: "function" as const,
2093
+ function: {
2094
+ name: fn.name,
2095
+ description: fn.description,
2096
+ parameters: convertGeminiSchemaToOpenAI(fn.parameters),
2097
+ },
2098
+ }))
2099
+ );
2100
+ }
2101
+
2102
+ // ─── Utility ────────────────────────────────────────────────────────────────
2103
+
2104
+ /** Returns a flat list of all tool names */
2105
+ export function getToolNames(): string[] {
2106
+ return getToolDeclarationsGemini().flatMap(g => g.functionDeclarations.map(f => f.name));
2107
+ }