@agenthifive/openclaw 0.1.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 (65) hide show
  1. package/README.md +124 -0
  2. package/dist/client.d.ts +27 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +136 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/index.d.ts +16 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +23 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/jwt-utils.d.ts +29 -0
  11. package/dist/jwt-utils.d.ts.map +1 -0
  12. package/dist/jwt-utils.js +55 -0
  13. package/dist/jwt-utils.js.map +1 -0
  14. package/dist/patch-verify.d.ts +28 -0
  15. package/dist/patch-verify.d.ts.map +1 -0
  16. package/dist/patch-verify.js +72 -0
  17. package/dist/patch-verify.js.map +1 -0
  18. package/dist/pending-approvals.d.ts +55 -0
  19. package/dist/pending-approvals.d.ts.map +1 -0
  20. package/dist/pending-approvals.js +95 -0
  21. package/dist/pending-approvals.js.map +1 -0
  22. package/dist/prompt-reference.d.ts +51 -0
  23. package/dist/prompt-reference.d.ts.map +1 -0
  24. package/dist/prompt-reference.js +645 -0
  25. package/dist/prompt-reference.js.map +1 -0
  26. package/dist/register.d.ts +20 -0
  27. package/dist/register.d.ts.map +1 -0
  28. package/dist/register.js +551 -0
  29. package/dist/register.js.map +1 -0
  30. package/dist/runtime.d.ts +66 -0
  31. package/dist/runtime.d.ts.map +1 -0
  32. package/dist/runtime.js +87 -0
  33. package/dist/runtime.js.map +1 -0
  34. package/dist/session-context.d.ts +39 -0
  35. package/dist/session-context.d.ts.map +1 -0
  36. package/dist/session-context.js +58 -0
  37. package/dist/session-context.js.map +1 -0
  38. package/dist/setup-wizard.d.ts +28 -0
  39. package/dist/setup-wizard.d.ts.map +1 -0
  40. package/dist/setup-wizard.js +303 -0
  41. package/dist/setup-wizard.js.map +1 -0
  42. package/dist/tools.d.ts +27 -0
  43. package/dist/tools.d.ts.map +1 -0
  44. package/dist/tools.js +128 -0
  45. package/dist/tools.js.map +1 -0
  46. package/dist/types.d.ts +93 -0
  47. package/dist/types.d.ts.map +1 -0
  48. package/dist/types.js +2 -0
  49. package/dist/types.js.map +1 -0
  50. package/dist/vault-action-proxy.d.ts +75 -0
  51. package/dist/vault-action-proxy.d.ts.map +1 -0
  52. package/dist/vault-action-proxy.js +152 -0
  53. package/dist/vault-action-proxy.js.map +1 -0
  54. package/dist/vault-provider.d.ts +52 -0
  55. package/dist/vault-provider.d.ts.map +1 -0
  56. package/dist/vault-provider.js +37 -0
  57. package/dist/vault-provider.js.map +1 -0
  58. package/dist/vault-token-manager.d.ts +42 -0
  59. package/dist/vault-token-manager.d.ts.map +1 -0
  60. package/dist/vault-token-manager.js +124 -0
  61. package/dist/vault-token-manager.js.map +1 -0
  62. package/openclaw.plugin.json +59 -0
  63. package/package.json +58 -0
  64. package/patches/README.md +85 -0
  65. package/patches/model-auth.patch +44 -0
@@ -0,0 +1,645 @@
1
+ /**
2
+ * AgentHiFive Vault Prompt Reference — Chunked Architecture
3
+ *
4
+ * Provides two modes of API reference injection:
5
+ *
6
+ * 1. **Inline mode** (`buildApiReferencePrompt`): Assembles a single prompt
7
+ * string from provider sections. Used by the MCP server and simple consumers.
8
+ *
9
+ * 2. **Chunked file mode** (`writeReferenceFiles`): Writes a base reference
10
+ * file (tools, permissions, action templates) plus per-service API reference
11
+ * files to a state directory. The system prompt then contains a lean pointer
12
+ * listing available files. Used by the OpenClaw plugin for better accuracy.
13
+ *
14
+ * Chunked > monolithic: eval showed 89% accuracy (chunked) vs 72% (monolithic)
15
+ * with realistic production-sized prompts. Models get overwhelmed when all
16
+ * provider API docs are in one blob.
17
+ */
18
+ import { writeFileSync } from "node:fs";
19
+ import { join } from "node:path";
20
+ // ---------------------------------------------------------------------------
21
+ // Provider-to-service aliasing (deduplicates docs for provider aliases)
22
+ // ---------------------------------------------------------------------------
23
+ const PROVIDER_TO_SERVICE = {
24
+ gmail: "google",
25
+ msteams: "microsoft",
26
+ };
27
+ // ---------------------------------------------------------------------------
28
+ // Inline Prompt Header/Footer (for simple buildApiReferencePrompt mode)
29
+ // ---------------------------------------------------------------------------
30
+ const PROMPT_HEADER = `## AgentHiFive API Reference
31
+
32
+ All external API calls go through the \`agenthifive.execute\` tool.
33
+ Authentication is handled automatically by the vault — do not add Authorization headers.
34
+
35
+ Before making API calls, use \`agenthifive.connections_list\` to discover available connections and their IDs.`;
36
+ const PROMPT_FOOTER = `### Notes
37
+ - All dates use ISO 8601 format (e.g., "2026-01-15T09:00:00Z")
38
+ - Google and Microsoft use the same connectionId for all their APIs (one OAuth connection covers mail + calendar + drive)
39
+ - If a request is blocked by policy, the error will indicate which URL patterns are allowed`;
40
+ // ---------------------------------------------------------------------------
41
+ // Inline per-provider sections (for simple buildApiReferencePrompt mode)
42
+ // ---------------------------------------------------------------------------
43
+ export const API_REFERENCE_SECTIONS = {
44
+ google: `### Google Gmail (provider: google)
45
+
46
+ List messages:
47
+ GET https://gmail.googleapis.com/gmail/v1/users/me/messages
48
+ query: { q: "search query", maxResults: "10", labelIds: "INBOX" }
49
+
50
+ Get message:
51
+ GET https://gmail.googleapis.com/gmail/v1/users/me/messages/{messageId}
52
+ query: { format: "full" }
53
+
54
+ Send message:
55
+ POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send
56
+ body: { raw: "<base64url-encoded RFC 2822 message>" }
57
+
58
+ Search operators: from:, to:, subject:, newer_than:1d, older_than:7d, has:attachment, is:unread
59
+
60
+ ### Google Calendar (provider: google)
61
+
62
+ List events:
63
+ GET https://www.googleapis.com/calendar/v3/calendars/primary/events
64
+ query: { timeMin: "ISO8601", timeMax: "ISO8601", singleEvents: "true", orderBy: "startTime" }
65
+
66
+ Create event:
67
+ POST https://www.googleapis.com/calendar/v3/calendars/primary/events
68
+ body: { summary: "...", start: { dateTime: "ISO8601" }, end: { dateTime: "ISO8601" }, attendees: [{ email: "..." }] }
69
+
70
+ ### Google Drive (provider: google)
71
+
72
+ List files:
73
+ GET https://www.googleapis.com/drive/v3/files
74
+ query: { q: "'root' in parents", fields: "files(id,name,mimeType,modifiedTime)", pageSize: "20" }
75
+
76
+ Get file metadata:
77
+ GET https://www.googleapis.com/drive/v3/files/{fileId}
78
+ query: { fields: "id,name,mimeType,size,modifiedTime,webViewLink" }`,
79
+ notion: `### Notion (provider: notion)
80
+
81
+ ⚠ Every request MUST include the Notion-Version header in the headers field:
82
+ headers: { "Notion-Version": "2022-06-28" }
83
+ ⚠ Notion uses POST for search and database queries — these are read operations despite using POST.
84
+
85
+ Search pages and databases:
86
+ POST https://api.notion.com/v1/search
87
+ body: { query: "search text" }
88
+
89
+ Get page:
90
+ GET https://api.notion.com/v1/pages/{pageId}
91
+
92
+ Get page property:
93
+ GET https://api.notion.com/v1/pages/{pageId}/properties/{propertyId}
94
+
95
+ Get database:
96
+ GET https://api.notion.com/v1/databases/{databaseId}
97
+
98
+ Query database:
99
+ POST https://api.notion.com/v1/databases/{databaseId}/query
100
+ body: { filter: { property: "Status", select: { equals: "Active" } }, sorts: [{ property: "Date", direction: "descending" }] }
101
+
102
+ List block children:
103
+ GET https://api.notion.com/v1/blocks/{blockId}/children
104
+
105
+ Create page:
106
+ POST https://api.notion.com/v1/pages
107
+ body: { parent: { database_id: "..." }, properties: { Name: { title: [{ text: { content: "..." } }] } } }
108
+
109
+ Update page:
110
+ PATCH https://api.notion.com/v1/pages/{pageId}
111
+ body: { properties: { Status: { select: { name: "Done" } } } }
112
+
113
+ Append blocks to page:
114
+ PATCH https://api.notion.com/v1/blocks/{blockId}/children
115
+ body: { children: [{ object: "block", type: "paragraph", paragraph: { rich_text: [{ text: { content: "..." } }] } }] }
116
+
117
+ List comments:
118
+ GET https://api.notion.com/v1/comments
119
+ query: { block_id: "..." }
120
+
121
+ Create comment:
122
+ POST https://api.notion.com/v1/comments
123
+ body: { parent: { page_id: "..." }, rich_text: [{ text: { content: "..." } }] }`,
124
+ microsoft: `### Microsoft Graph — Mail (provider: microsoft)
125
+
126
+ List messages:
127
+ GET https://graph.microsoft.com/v1.0/me/messages
128
+ query: { "$top": "10", "$orderby": "receivedDateTime desc", "$select": "subject,from,receivedDateTime,bodyPreview" }
129
+
130
+ Send mail:
131
+ POST https://graph.microsoft.com/v1.0/me/sendMail
132
+ body: { message: { subject: "...", body: { contentType: "Text", content: "..." }, toRecipients: [{ emailAddress: { address: "..." } }] } }
133
+
134
+ ### Microsoft Graph — Calendar (provider: microsoft)
135
+
136
+ List events:
137
+ GET https://graph.microsoft.com/v1.0/me/calendarView
138
+ query: { startDateTime: "ISO8601", endDateTime: "ISO8601", "$select": "subject,start,end,location,organizer" }
139
+
140
+ ### Microsoft Graph — Files (provider: microsoft)
141
+
142
+ List files:
143
+ GET https://graph.microsoft.com/v1.0/me/drive/root/children
144
+ query: { "$select": "id,name,size,lastModifiedDateTime,webUrl" }`,
145
+ };
146
+ // ---------------------------------------------------------------------------
147
+ // Inline prompt builder (for MCP server / simple consumers)
148
+ // ---------------------------------------------------------------------------
149
+ /**
150
+ * Build an API reference prompt from the given provider names.
151
+ *
152
+ * Only includes sections for providers that have entries in
153
+ * `API_REFERENCE_SECTIONS`. Unknown providers are silently ignored.
154
+ */
155
+ export function buildApiReferencePrompt(providers) {
156
+ const sections = [];
157
+ for (const provider of providers) {
158
+ const section = API_REFERENCE_SECTIONS[provider];
159
+ if (section) {
160
+ sections.push(section);
161
+ }
162
+ }
163
+ if (sections.length === 0) {
164
+ return "";
165
+ }
166
+ return [PROMPT_HEADER, ...sections, PROMPT_FOOTER].join("\n\n");
167
+ }
168
+ /**
169
+ * Full API reference prompt with all providers.
170
+ * Kept for backwards compatibility and programmatic access.
171
+ */
172
+ export const API_REFERENCE_PROMPT = buildApiReferencePrompt(Object.keys(API_REFERENCE_SECTIONS));
173
+ // ===========================================================================
174
+ // CHUNKED FILE MODE (for OpenClaw plugin)
175
+ // ===========================================================================
176
+ // ---------------------------------------------------------------------------
177
+ // Base reference sections (no provider API docs — those go in per-service files)
178
+ // ---------------------------------------------------------------------------
179
+ const SECTION_HEADER = `# AgentHiFive Vault Reference
180
+
181
+ This file is auto-generated at startup. It describes how to use the AgentHiFive vault
182
+ to access external services (email, calendar, files, messaging).
183
+
184
+ Authentication is handled automatically by the vault — never add Authorization headers yourself.`;
185
+ const SECTION_TOOLS = `## Available Tools
186
+
187
+ | Tool | Purpose |
188
+ |------|---------|
189
+ | \`request_permission\` | Request access to a service (workspace owner approves in dashboard) |
190
+ | \`vault_execute\` | Make an API call through the vault proxy — vault injects credentials, enforces policies, logs audit |
191
+ | \`vault_download\` | Download a file through the vault and save to disk — returns local file path. Use for binary downloads (Drive files, attachments, images) |
192
+ | \`vault_connections_list\` | List active connections (service name, label, status) |
193
+ | \`vault_await_approval\` | (Fallback) Block until a step-up approval resolves. Only use if the user explicitly asks you to wait — the system auto-notifies you by default |
194
+ | \`list_approvals\` | Check status of step-up approval requests (pending, approved, denied, expired, consumed) |`;
195
+ const SECTION_PERMISSION_FLOW = `## How It Works
196
+
197
+ 1. **Request access:** Call \`request_permission\` with:
198
+ - \`actionTemplateId\`: the capability ID (see table below)
199
+ - \`reason\`: why you need it (e.g., "User asked me to send a Telegram message")
200
+ 2. **Tell the user** you've requested access and they need to approve it in the AgentHiFive dashboard
201
+ 3. **After approval, make API calls:** Call \`vault_execute\` with:
202
+ - \`service\`: the service name (e.g., \`"telegram"\`, \`"anthropic-messages"\`, \`"gmail"\`)
203
+ - \`method\`: HTTP method (GET, POST, PUT, DELETE, PATCH)
204
+ - \`url\`: the provider API URL (see API Reference below)
205
+ - \`body\`: request body (for POST/PUT/PATCH)
206
+ - For multi-account services (Google, Microsoft), also pass \`connectionId\` from \`vault_connections_list\`
207
+
208
+ ### Step-Up Approvals
209
+
210
+ Some actions require per-request approval (e.g., downloading files, sending emails in strict mode).
211
+ When \`vault_execute\` returns \`approvalRequired: true\` (HTTP 202):
212
+
213
+ 1. **Note the \`approvalRequestId\`** from the response
214
+ 2. **Tell the user** this specific action needs approval in the AgentHiFive dashboard
215
+ 3. **Do NOT call \`vault_await_approval\`** — the system will automatically notify you when the approval resolves. You will receive an approval notification with the approvalId when the user approves (or a denial/expiry notice).
216
+ 4. **When you receive the notification**, re-submit the **exact same request** with \`approvalId\` set to the \`approvalRequestId\`:
217
+ \`vault_execute({ ...sameParams, approvalId: "the-approval-id" })\`
218
+ 5. **If denied or expired**, inform the user and do not retry.
219
+
220
+ The approval is single-use: once consumed by a successful re-submit, it cannot be reused.
221
+
222
+ **Important:** Never add Authorization headers — the vault injects credentials automatically.
223
+ Never modify \`credentials.vault.connections\` in openclaw.json — the vault resolves connections automatically by service name.`;
224
+ const SECTION_ACTION_TEMPLATES = `## Action Template IDs
225
+
226
+ | ID | Service | Description |
227
+ |----|---------|-------------|
228
+ | \`gmail-read\` | Google Gmail | Read messages and search |
229
+ | \`gmail-manage\` | Google Gmail | Read, send, compose drafts, manage labels |
230
+ | \`calendar-read\` | Google Calendar | Read events |
231
+ | \`calendar-manage\` | Google Calendar | Read, create, edit, delete events |
232
+ | \`drive-read\` | Google Drive | Read files |
233
+ | \`drive-manage\` | Google Drive | Read, upload, edit, delete files |
234
+ | \`docs-read\` | Google Docs | Read documents |
235
+ | \`docs-manage\` | Google Docs | Read, create, edit documents |
236
+ | \`sheets-read\` | Google Sheets | Read spreadsheets |
237
+ | \`sheets-manage\` | Google Sheets | Read, create, edit spreadsheets |
238
+ | \`contacts-read\` | Google Contacts | Read contacts and contact groups |
239
+ | \`contacts-manage\` | Google Contacts | Read, create, edit, delete contacts |
240
+ | \`outlook-read\` | Microsoft Outlook | Read mail |
241
+ | \`outlook-manage\` | Microsoft Outlook | Read, send, manage emails |
242
+ | \`outlook-calendar-read\` | Outlook Calendar | Read events |
243
+ | \`outlook-calendar-manage\` | Outlook Calendar | Read, create, edit, delete events |
244
+ | \`outlook-contacts-read\` | Outlook Contacts | Read contacts |
245
+ | \`outlook-contacts-manage\` | Outlook Contacts | Read, create, edit, delete contacts |
246
+ | \`onedrive-read\` | OneDrive | Read files |
247
+ | \`onedrive-manage\` | OneDrive | Read, upload, edit, delete files |
248
+ | \`teams-read\` | Microsoft Teams | Read messages |
249
+ | \`teams-manage\` | Microsoft Teams | Read, send messages, manage files |
250
+ | \`slack\` | Slack | Read/send messages, upload files, manage reactions |
251
+ | \`telegram\` | Telegram | Send/receive messages via bot |
252
+ | \`anthropic-messages\` | Anthropic | Claude LLM API |
253
+ | \`openai\` | OpenAI | Chat completions, embeddings, model listing |
254
+ | \`gemini\` | Google Gemini | Content generation, embeddings, model listing |
255
+ | \`openrouter\` | OpenRouter | Chat completions and model listing |
256
+ | \`notion-read\` | Notion | Read pages, databases, blocks, and search |
257
+ | \`notion-manage\` | Notion | Read, create, update pages and databases |
258
+ | \`trello-read\` | Trello | Read boards, lists, cards, and labels |
259
+ | \`trello-manage\` | Trello | Read, create, update cards and lists |
260
+ | \`jira-read\` | Jira | Search and read issues, projects, and comments |
261
+ | \`jira-manage\` | Jira | Read, create, update issues and comments |`;
262
+ const SECTION_NOTES = `## Notes
263
+
264
+ - **Vault-managed channels need NO tokens.** Telegram, Slack, and other vault-managed channels do NOT need bot tokens, app tokens, or API keys in openclaw.json. The vault injects all credentials automatically. NEVER ask users for tokens for vault-managed services.
265
+ - **Do NOT use the built-in \`message\` tool for vault-managed channels.** The \`message\` tool uses OpenClaw's native channel routing, which does not have real credentials for vault-managed services. Always use \`vault_execute\` to send messages to Telegram, Slack, and other vault-managed channels. The \`message\` tool will return 404 errors for these channels.
266
+ - **Enabling a channel:** Use \`config.patch\` to add a minimal \`channels.<name>\` section and set \`plugins.entries.<name>.enabled: true\`. The vault channel watcher will auto-start the poller.
267
+ - Telegram: \`{ channels: { telegram: { botToken: "vault-managed", dmPolicy: "open", allowFrom: ["*"] } }, plugins: { entries: { telegram: { enabled: true } } } }\`
268
+ - Slack: \`{ channels: { slack: {} }, plugins: { entries: { slack: { enabled: true } } } }\`
269
+ - **Disabling a channel:** Use \`config.patch\` with \`{ plugins: { entries: { <name>: { enabled: false } } } }\`. The poller stops within 60 seconds.
270
+ - **Auto-polling:** When a channel is enabled and the vault has an active connection, incoming messages are automatically polled and dispatched to you. You do NOT need to call getUpdates or conversations.history yourself for receiving messages.
271
+ - **No connection UUIDs for singletons:** Telegram, Slack, Anthropic, OpenAI, Gemini, and OpenRouter are singletons (one connection per workspace). Always use \`service: "telegram"\`, \`service: "slack"\`, \`service: "anthropic-messages"\`, \`service: "openai"\`, \`service: "gemini"\`, or \`service: "openrouter"\` — never use a connection UUID for these.
272
+ - Multi-account services (Google, Microsoft) may have multiple connections — use \`connectionId\` from \`vault_connections_list\` only for these.
273
+ - All dates use ISO 8601 format (e.g., "2026-01-15T09:00:00Z")
274
+ - Do NOT modify \`credentials.vault.connections\` in openclaw.json — the vault resolves connections automatically by service name.
275
+
276
+ ## Retry & Failure Rules
277
+
278
+ - **Never retry the same \`vault_execute\` call more than once.** If the same call fails twice (original + 1 retry), stop and explain the error to the user. Do NOT keep trying.
279
+ - **Empty or missing response body:** If a \`vault_execute\` call returns an empty body, no data, or times out, treat it as a definitive failure. Do NOT retry. Report the issue to the user immediately.
280
+ - **Binary file downloads:** \`vault_execute\` cannot return binary data — it returns a \`_binaryContent\` metadata object instead. Before downloading any file, check its content type or mimeType. Prefer structured API access (JSON) over binary downloads whenever possible. When binary is unavoidable, use \`vault_download\` — it saves the file to disk and returns the local file path.
281
+ - **Large responses saved to disk:** When a \`vault_execute\` response exceeds ~50KB (e.g., JSON with embedded base64 attachments), the full JSON is automatically saved to disk. The result includes \`responseSavedToDisk: true\`, \`path\` (local file), \`preview\` (structural summary), and \`hint\`. Read the file with your file tools to access specific fields. This prevents large payloads from filling your context window.
282
+
283
+ ## Error Handling
284
+
285
+ When the vault blocks a request, the response includes an \`error\` field and often a \`hint\` field. **Always read the hint** — it tells you exactly how to fix your request:
286
+ - **Wrong HTTP method:** hint lists the allowed methods (e.g., "Allowed methods: POST" — common with Slack)
287
+ - **Wrong URL path:** hint lists allowed path patterns
288
+ - **Wrong host:** hint lists which hosts have allowlist rules
289
+ - **Rate limited:** hint tells you when to retry (\`retryAfter\` seconds)
290
+ - **Model mismatch:** hint tells you which execution model to use instead
291
+ - **No permission:** call \`request_permission\` to request the needed capability
292
+ - **Step-up required (202):** hint tells you to re-submit with \`approvalId\` after user approval
293
+ - **Download blocked by approval:** If \`vault_download\` returns a step-up approval (202), tell the user their options:
294
+ 1. Approve the download in the AgentHiFive dashboard (you'll retry with the \`approvalId\`)
295
+ 2. If the file is a convertible type (documents, spreadsheets, PDFs, presentations), you can try the copy-convert approach instead — it reads content through structured APIs without a binary download, which may not require the same approval`;
296
+ // ---------------------------------------------------------------------------
297
+ // Per-provider API sections (chunked — one file per service group)
298
+ // ---------------------------------------------------------------------------
299
+ export const CHUNKED_API_SECTIONS = {
300
+ google: `### Google APIs (provider: google, multi-account — use connectionId)
301
+
302
+ Google APIs use standard REST: GET for reads, POST for writes. Use \`connectionId\` from \`vault_connections_list\`.
303
+
304
+ **Gmail** (base: \`https://gmail.googleapis.com\`):
305
+ - List messages: \`GET /gmail/v1/users/me/messages\` — query: \`{ q, maxResults, labelIds }\`
306
+ - Get message: \`GET /gmail/v1/users/me/messages/{id}\` — query: \`{ format: "full" }\`
307
+ - Get attachment: \`GET /gmail/v1/users/me/messages/{id}/attachments/{attachmentId}\`
308
+ - List labels: \`GET /gmail/v1/users/me/labels\`
309
+ - Send message: \`POST /gmail/v1/users/me/messages/send\` — body: \`{ raw: "<base64url-encoded RFC 2822 email>" }\`
310
+ ⚠ The \`raw\` field must be a base64url-encoded string of a complete RFC 2822 email (with To, Subject, MIME headers).
311
+ - Search operators: \`from:\`, \`to:\`, \`subject:\`, \`newer_than:1d\`, \`has:attachment\`, \`is:unread\`
312
+ - Policy notes (vary by user's security tier — vault returns a hint if a rule blocks you):
313
+ - **Strict read**: only messages with \`newer_than:Xd\` (1–7 days) in the query are allowed without approval; always include a recency filter
314
+ - **Balanced read/manage**: attachment requests (\`/attachments/\`) require the user's approval
315
+ - **Strict manage**: same read constraints apply; sends and drafts also require approval; deletions are blocked
316
+
317
+ **Calendar** (base: \`https://www.googleapis.com\`):
318
+ - List events: \`GET /calendar/v3/calendars/primary/events\` — query: \`{ timeMin, timeMax, singleEvents, orderBy }\`
319
+ - Create event: \`POST /calendar/v3/calendars/primary/events\` — body: \`{ summary, start, end, attendees }\`
320
+ - Policy notes:
321
+ - **Strict read**: listing events is allowed; reading individual event details requires approval
322
+ - **Strict manage**: same read constraints; event creation/modification requires approval; deletion is blocked
323
+ - **Balanced**: all reads allowed; writes allowed within rate limits
324
+
325
+ **Drive** (base: \`https://www.googleapis.com\`):
326
+ - List files: \`GET /drive/v3/files\` — query: \`{ q, fields, pageSize }\`
327
+ - Get file metadata: \`GET /drive/v3/files/{id}\` — query: \`{ fields: "id,name,mimeType,size,webViewLink" }\`
328
+ - Export Google-native file: \`GET /drive/v3/files/{id}/export\` — query: \`{ mimeType: "text/csv" }\` (or \`text/plain\`, \`application/pdf\`)
329
+ - Copy file: \`POST /drive/v3/files/{id}/copy\` — body: \`{ mimeType: "application/vnd.google-apps.spreadsheet" }\`
330
+ - Download binary file: \`vault_download({ url: "https://www.googleapis.com/drive/v3/files/{id}?alt=media", connectionId: "..." })\`
331
+ - Policy notes:
332
+ - **Strict**: binary downloads (\`alt=media\`) are blocked; use copy-convert or export instead. File listing is allowed; metadata reads require approval
333
+ - **Balanced**: binary downloads require approval; all reads allowed; writes allowed within rate limits
334
+ - **Minimal**: all operations allowed within rate limits
335
+
336
+ **Reading file contents — decision tree:**
337
+ 1. Get file metadata first (\`GET /drive/v3/files/{id}?fields=mimeType,name\`)
338
+ 2. **Google-native file** (\`application/vnd.google-apps.*\`):
339
+ - Spreadsheet → use Sheets API via \`vault_execute\` (returns structured JSON)
340
+ - Document → use Docs API via \`vault_execute\` (returns structured JSON)
341
+ - Or export: \`GET /drive/v3/files/{id}/export?mimeType=text/csv\` via \`vault_execute\`
342
+ 3. **Convertible file** — copy-convert to a Google-native format, then read via structured API:
343
+ - \`POST /drive/v3/files/{id}/copy\` with body \`{ mimeType: "<target>" }\`
344
+ - Read the converted copy via the appropriate API (\`vault_execute\`), then delete the copy
345
+ - Target mimeType by source:
346
+ - \`.xls\`, \`.xlsx\`, \`.csv\`, \`.ods\` → \`application/vnd.google-apps.spreadsheet\` → read via Sheets API
347
+ - \`.docx\`, \`.doc\`, \`.rtf\`, \`.odt\`, \`.html\`, \`.txt\` → \`application/vnd.google-apps.document\` → read via Docs API
348
+ - \`.pptx\`, \`.ppt\`, \`.odp\` → \`application/vnd.google-apps.presentation\` → export as \`text/plain\`
349
+ - PDF → \`application/vnd.google-apps.document\` (OCR) → read via Docs API or export as \`text/plain\`
350
+ - Images (when extracting text) → \`application/vnd.google-apps.document\` (OCR)
351
+ - Prefer this over \`vault_download\` whenever you need text content — it avoids binary transfer entirely
352
+ - OCR quality varies: works well for text-heavy PDFs/images, poorly for scans or complex layouts
353
+ 4. **Non-convertible binary** (audio, video, archives, proprietary formats) → use \`vault_download\`
354
+ 5. **Never** use \`vault_execute\` with \`alt=media\` — binary data cannot pass through JSON serialization
355
+
356
+ **Docs** (base: \`https://docs.googleapis.com\`):
357
+ - Get document: \`GET /v1/documents/{documentId}\`
358
+ - Create document: \`POST /v1/documents\` — body: \`{ title: "..." }\`
359
+ - Batch update: \`POST /v1/documents/{documentId}:batchUpdate\` — body: \`{ requests: [...] }\`
360
+ Common requests: \`insertText\` (\`{ location: { index }, text }\`), \`deleteContentRange\` (\`{ range: { startIndex, endIndex } }\`), \`replaceAllText\` (\`{ containsText: { text, matchCase }, replaceText }\`)
361
+
362
+ **Sheets** (base: \`https://sheets.googleapis.com\`):
363
+ - Get spreadsheet: \`GET /v4/spreadsheets/{spreadsheetId}\` — query: \`{ ranges, includeGridData }\`
364
+ - Read values: \`GET /v4/spreadsheets/{spreadsheetId}/values/{range}\` — range format: \`Sheet1!A1:D10\`
365
+ - Update values: \`PUT /v4/spreadsheets/{spreadsheetId}/values/{range}\` — query: \`{ valueInputOption: "USER_ENTERED" }\` — body: \`{ values: [[...]] }\`
366
+ - Append rows: \`POST /v4/spreadsheets/{spreadsheetId}/values/{range}:append\` — query: \`{ valueInputOption: "USER_ENTERED" }\` — body: \`{ values: [[...]] }\`
367
+ - Create spreadsheet: \`POST /v4/spreadsheets\` — body: \`{ properties: { title: "..." } }\`
368
+
369
+ **Contacts** (base: \`https://people.googleapis.com\`):
370
+ - List contacts: \`GET /v1/people/me/connections\` — query: \`{ personFields: "names,emailAddresses,phoneNumbers", pageSize: 100 }\`
371
+ - Get contact: \`GET /v1/people/{resourceName}\` — query: \`{ personFields: "names,emailAddresses,phoneNumbers,addresses,organizations" }\`
372
+ - resourceName format: \`people/c1234567890\`
373
+ - Search contacts: \`GET /v1/people:searchContacts\` — query: \`{ query: "search term", readMask: "names,emailAddresses" }\`
374
+ - Create contact: \`POST /v1/people:createContact\` — body: \`{ names: [{ givenName, familyName }], emailAddresses: [{ value }], phoneNumbers: [{ value }] }\`
375
+ - Update contact: \`PATCH /v1/people/{resourceName}:updateContact\` — query: \`{ updatePersonFields: "names,emailAddresses" }\` — body: same as create
376
+ - Delete contact: \`DELETE /v1/people/{resourceName}:deleteContact\`
377
+ - List contact groups: \`GET /v1/contactGroups\`
378
+ - ⚠ Always include \`personFields\` (for get/list) or \`readMask\` (for search) — the API returns no data without it
379
+ - Policy notes:
380
+ - **Minimal**: notes/biographies stripped, all other fields visible
381
+ - **Balanced**: PII fields (phone numbers, addresses, birthdays) stripped by default. To access full fields for a **specific contact**, add \`requestFullFields: true\` to vault_execute — this triggers step-up approval. Only works on individual contact endpoints (e.g. \`GET /v1/people/c1234567890\`), NOT on list or search endpoints. Once approved, re-submit with both \`approvalId\` and \`requestFullFields: true\`. Notes remain stripped even with approval.
382
+ - **Strict**: PII fields always stripped, no way to request them. Deletion is blocked on manage connections.`,
383
+ gmail: ``, // covered by "google"
384
+ microsoft: `### Microsoft Graph APIs (provider: microsoft, multi-account — use connectionId)
385
+
386
+ Microsoft Graph uses standard REST: GET for reads, POST for writes. Use \`connectionId\` from \`vault_connections_list\`.
387
+ Base URL: \`https://graph.microsoft.com/v1.0\`
388
+ ⚠ Query parameters use OData syntax with \`$\` prefix: \`$top\`, \`$select\`, \`$orderby\`, \`$filter\`.
389
+
390
+ **Mail:**
391
+ - List messages: \`GET /me/messages\` — query: \`{ $top, $orderby, $select }\`
392
+ - Send mail: \`POST /me/sendMail\` — body: \`{ message: { subject, body: { contentType, content }, toRecipients: [{ emailAddress: { address } }] } }\`
393
+ - Policy notes:
394
+ - **Strict read**: listing messages is allowed; reading message body/attachments requires approval
395
+ - **Strict manage**: same read constraints; sending mail requires approval; deletion is blocked
396
+ - **Balanced**: all reads allowed; attachment downloads require approval
397
+
398
+ **Calendar:**
399
+ - List events: \`GET /me/calendarView\` — query: \`{ startDateTime, endDateTime, $select }\`
400
+ - Create event: \`POST /me/events\` — body: \`{ subject, start, end, attendees }\`
401
+ - Policy notes:
402
+ - **Strict read**: listing events is allowed; reading individual event details requires approval
403
+ - **Strict manage**: same read constraints; event creation/modification requires approval; deletion is blocked
404
+ - **Balanced**: all reads allowed; writes allowed within rate limits
405
+
406
+ **Teams:**
407
+ - List chats: \`GET /me/chats\` — query: \`{ $top }\`
408
+ - Read messages: \`GET /me/chats/{chatId}/messages\` — query: \`{ $top }\`
409
+ - Send to chat: \`POST /me/chats/{chatId}/messages\` — body: \`{ body: { content } }\`
410
+ - Send to channel: \`POST /teams/{teamId}/channels/{channelId}/messages\` — body: \`{ body: { content } }\`
411
+
412
+ **Contacts:**
413
+ - List contacts: \`GET /me/contacts\` — query: \`{ $top, $select, $orderby }\`
414
+ - Get contact: \`GET /me/contacts/{contactId}\`
415
+ - Search people: \`GET /me/people\` — query: \`{ $search: "name", $top }\`
416
+ - Create contact: \`POST /me/contacts\` — body: \`{ givenName, surname, emailAddresses: [{ address, name }], businessPhones: ["..."] }\`
417
+ - Update contact: \`PATCH /me/contacts/{contactId}\` — body: updated fields
418
+ - Delete contact: \`DELETE /me/contacts/{contactId}\`
419
+ - List contact folders: \`GET /me/contactFolders\`
420
+ - Policy notes:
421
+ - **Minimal**: notes stripped, all other fields visible
422
+ - **Balanced**: PII fields (phone numbers, addresses, birthdays) stripped by default. To access full fields for a **specific contact**, add \`requestFullFields: true\` to vault_execute — only works on individual contact endpoints (e.g. \`GET /v1.0/me/contacts/{id}\`), NOT on list endpoints. Once approved, re-submit with both \`approvalId\` and \`requestFullFields: true\`. Notes remain stripped.
423
+ - **Strict**: PII fields always stripped, no way to request them. Deletion is blocked on manage connections.
424
+
425
+ **Files (OneDrive):**
426
+ - List root files: \`GET /me/drive/root/children\` — query: \`{ $select, $top, $orderby }\`
427
+ - List folder contents: \`GET /me/drive/items/{itemId}/children\`
428
+ - Get file metadata: \`GET /me/drive/items/{itemId}\` — query: \`{ $select: "id,name,file,size,webUrl" }\`
429
+ - Search files: \`GET /me/drive/root/search(q='{query}')\`
430
+ - Upload small file (<4MB): \`PUT /me/drive/root:/{path}:/content\` — body: file bytes, header: \`Content-Type: application/octet-stream\`
431
+ - Create folder: \`POST /me/drive/root/children\` — body: \`{ name, folder: {}, "@microsoft.graph.conflictBehavior": "rename" }\`
432
+ - Delete item: \`DELETE /me/drive/items/{itemId}\`
433
+ - Move/rename: \`PATCH /me/drive/items/{itemId}\` — body: \`{ name, parentReference: { id } }\`
434
+ - Create sharing link: \`POST /me/drive/items/{itemId}/createLink\` — body: \`{ type: "view"|"edit", scope: "organization"|"anonymous" }\`
435
+ - Invite collaborator: \`POST /me/drive/items/{itemId}/invite\` — body: \`{ recipients: [{ email }], roles: ["read"|"write"] }\`
436
+
437
+ **Excel Workbook API** (read spreadsheet content as structured JSON — no binary download):
438
+ - List worksheets: \`GET /me/drive/items/{itemId}/workbook/worksheets\`
439
+ - Read used range: \`GET /me/drive/items/{itemId}/workbook/worksheets/{sheetName}/usedRange\` — returns \`{ values: [[...]] }\`
440
+ - Read specific range: \`GET /me/drive/items/{itemId}/workbook/worksheets/{sheetName}/range(address='A1:Z100')\`
441
+ - Update cells: \`PATCH /me/drive/items/{itemId}/workbook/worksheets/{sheetName}/range(address='A1:B2')\` — body: \`{ values: [["a","b"],["c","d"]] }\`
442
+ - Add rows: \`POST /me/drive/items/{itemId}/workbook/tables/{tableId}/rows\` — body: \`{ values: [["a","b"]] }\`
443
+
444
+ **Reading OneDrive file contents — decision tree:**
445
+ 1. Get file metadata first: \`GET /me/drive/items/{id}?$select=name,file,size\` — check \`file.mimeType\`
446
+ 2. **Excel files** (\`.xlsx\`, \`.xls\`, \`.csv\` opened in Excel) → Use the Excel Workbook API above via \`vault_execute\` — returns structured JSON with cell values, no binary download needed
447
+ 3. **Text files** (\`.txt\`, \`.csv\`, \`.json\`, \`.md\`, \`.log\`, \`.xml\`, \`.html\`) → \`vault_download\` to save to disk, then read the local file
448
+ 4. **Other binary files** (images, PDFs, \`.docx\`, \`.pptx\`, audio, video) → \`vault_download\`
449
+ 5. **Never** use \`vault_execute\` with \`/content\` — the \`/content\` endpoint returns binary data via a redirect to SharePoint, which cannot pass through JSON serialization. You will get a \`_binaryContent\` metadata response instead of actual file content.
450
+ 6. **Do NOT use Google APIs** (Sheets, Docs, Drive) to read OneDrive files — they are different providers.
451
+
452
+ - Policy notes (OneDrive):
453
+ - **Strict**: binary downloads (\`/content\`) are blocked; use the Excel Workbook API for spreadsheets. File listing is allowed; metadata reads require approval
454
+ - **Balanced**: binary downloads require approval; all reads allowed; writes allowed within rate limits
455
+ - **Minimal**: all operations allowed within rate limits`,
456
+ msteams: ``, // covered by "microsoft"
457
+ slack: `### Slack API (provider: slack, singleton)
458
+
459
+ **⚠ ALL Slack API methods use POST — never use GET.** This is not standard REST. Even read operations
460
+ (conversations.history, users.info) require POST with parameters in the JSON body, not query strings.
461
+
462
+ Use \`service: "slack"\` with \`vault_execute\`. The vault injects the bot token as a Bearer header automatically.
463
+
464
+ **Read operations (all POST):**
465
+ - Read messages: \`POST https://slack.com/api/conversations.history\` — body: \`{ channel, limit }\`
466
+ - Read thread: \`POST https://slack.com/api/conversations.replies\` — body: \`{ channel, ts }\`
467
+ - List channels: \`POST https://slack.com/api/conversations.list\` — body: \`{ types, limit }\`
468
+ - Get user info: \`POST https://slack.com/api/users.info\` — body: \`{ user }\`
469
+ - List emoji: \`POST https://slack.com/api/emoji.list\`
470
+ - List pins: \`POST https://slack.com/api/pins.list\` — body: \`{ channel }\`
471
+
472
+ **Write operations (all POST):**
473
+ - Send message: \`POST https://slack.com/api/chat.postMessage\` — body: \`{ channel, text, blocks }\`
474
+ - Update message: \`POST https://slack.com/api/chat.update\` — body: \`{ channel, ts, text }\`
475
+ - Delete message: \`POST https://slack.com/api/chat.delete\` — body: \`{ channel, ts }\`
476
+ - Upload file: \`POST https://slack.com/api/files.uploadV2\` — body: \`{ channel_id, content, filename }\`
477
+ - Add reaction: \`POST https://slack.com/api/reactions.add\` — body: \`{ channel, timestamp, name }\`
478
+
479
+ Wrong: \`GET https://slack.com/api/conversations.history?channel=C123&limit=10\`
480
+ Right: \`POST https://slack.com/api/conversations.history\` with body \`{ "channel": "C123", "limit": 10 }\``,
481
+ telegram: `### Telegram Bot API (provider: telegram, singleton)
482
+
483
+ **URL format:** \`https://api.telegram.org/bot/<method>\` — the vault injects the real bot token automatically.
484
+ Do NOT put a token or placeholder in the URL (NOT \`/bot{token}/sendMessage\`, NOT \`/bot123:ABC/sendMessage\`).
485
+
486
+ **Write operations (POST):**
487
+ - Send message: \`POST https://api.telegram.org/bot/sendMessage\` — body: \`{ chat_id, text, parse_mode }\`
488
+ - Send photo: \`POST https://api.telegram.org/bot/sendPhoto\` — body: \`{ chat_id, photo }\`
489
+ - Forward message: \`POST https://api.telegram.org/bot/forwardMessage\` — body: \`{ chat_id, from_chat_id, message_id }\`
490
+
491
+ **Read operations (GET):**
492
+ - Get updates: \`GET https://api.telegram.org/bot/getUpdates\` — query: \`{ offset, timeout }\`
493
+ - Get chat: \`GET https://api.telegram.org/bot/getChat\` — query: \`{ chat_id }\`
494
+
495
+ Use \`service: "telegram"\` with \`vault_execute\`. Never use a connectionId for Telegram.
496
+ Incoming messages are auto-polled — you do NOT need to call getUpdates yourself.`,
497
+ notion: `### Notion API (provider: notion, multi-account — use connectionId)
498
+
499
+ **Base URL:** \`https://api.notion.com\`
500
+ ⚠ **Every request MUST include the \`Notion-Version\` header.** Pass it in the \`headers\` field:
501
+ \`headers: { "Notion-Version": "2022-06-28" }\`
502
+
503
+ ⚠ **Notion uses POST for some read operations** (search, database queries). These are NOT writes — they just happen to use POST.
504
+
505
+ Use \`connectionId\` from \`vault_connections_list\`. Notion is NOT a singleton — each user authorizes their own workspace.
506
+
507
+ **Read operations:**
508
+ - Search pages & databases: \`POST https://api.notion.com/v1/search\` — body: \`{ query: "search text" }\`
509
+ - Get page: \`GET https://api.notion.com/v1/pages/{page_id}\`
510
+ - Get page property: \`GET https://api.notion.com/v1/pages/{page_id}/properties/{property_id}\`
511
+ - Get database: \`GET https://api.notion.com/v1/databases/{database_id}\`
512
+ - Query database: \`POST https://api.notion.com/v1/databases/{database_id}/query\` — body: \`{ filter: {...}, sorts: [...] }\`
513
+ - Get block: \`GET https://api.notion.com/v1/blocks/{block_id}\`
514
+ - List block children: \`GET https://api.notion.com/v1/blocks/{block_id}/children\`
515
+ - List comments: \`GET https://api.notion.com/v1/comments\` — query: \`{ block_id }\`
516
+ - List users: \`GET https://api.notion.com/v1/users\`
517
+
518
+ **Write operations:**
519
+ - Create page: \`POST https://api.notion.com/v1/pages\` — body: \`{ parent: { database_id: "..." }, properties: {...} }\`
520
+ - Update page: \`PATCH https://api.notion.com/v1/pages/{page_id}\` — body: \`{ properties: {...} }\`
521
+ - Update block: \`PATCH https://api.notion.com/v1/blocks/{block_id}\` — body: \`{ type: {...} }\`
522
+ - Append blocks: \`PATCH https://api.notion.com/v1/blocks/{block_id}/children\` — body: \`{ children: [...] }\`
523
+ - Delete block: \`DELETE https://api.notion.com/v1/blocks/{block_id}\`
524
+ - Create comment: \`POST https://api.notion.com/v1/comments\` — body: \`{ parent: { page_id: "..." }, rich_text: [...] }\`
525
+
526
+ **Common property formats:**
527
+ - Title: \`{ title: [{ text: { content: "..." } }] }\`
528
+ - Rich text: \`{ rich_text: [{ text: { content: "..." } }] }\`
529
+ - Select: \`{ select: { name: "Option" } }\`
530
+ - Date: \`{ date: { start: "2026-01-15" } }\`
531
+ - Checkbox: \`{ checkbox: true }\``,
532
+ trello: `### Trello API (provider: trello, multi-account — use connectionId)
533
+
534
+ **Base URL:** \`https://api.trello.com\`
535
+ ⚠ **Do NOT include \`key\` or \`token\` query parameters** — the vault injects both automatically.
536
+
537
+ Use \`connectionId\` from \`vault_connections_list\`. Trello is NOT a singleton — each user connects their own account.
538
+
539
+ **Read operations (GET):**
540
+ - List boards: \`GET https://api.trello.com/1/members/me/boards\`
541
+ - Get board: \`GET https://api.trello.com/1/boards/{boardId}\`
542
+ - Board lists: \`GET https://api.trello.com/1/boards/{boardId}/lists\`
543
+ - Board cards: \`GET https://api.trello.com/1/boards/{boardId}/cards\`
544
+ - Board labels: \`GET https://api.trello.com/1/boards/{boardId}/labels\`
545
+ - Get list: \`GET https://api.trello.com/1/lists/{listId}\`
546
+ - List cards: \`GET https://api.trello.com/1/lists/{listId}/cards\`
547
+ - Get card: \`GET https://api.trello.com/1/cards/{cardId}\`
548
+ - Card activity: \`GET https://api.trello.com/1/cards/{cardId}/actions\`
549
+ - Card attachments: \`GET https://api.trello.com/1/cards/{cardId}/attachments\`
550
+ - Card checklists: \`GET https://api.trello.com/1/cards/{cardId}/checklists\`
551
+
552
+ **Write operations:**
553
+ - Create card: \`POST https://api.trello.com/1/cards\` — body: \`{ name, idList, desc }\`
554
+ - Update card: \`PUT https://api.trello.com/1/cards/{cardId}\` — body: \`{ name, desc, closed, idList }\`
555
+ - Delete card: \`DELETE https://api.trello.com/1/cards/{cardId}\`
556
+ - Archive card: \`PUT https://api.trello.com/1/cards/{cardId}\` — body: \`{ closed: true }\`
557
+ - Add comment: \`POST https://api.trello.com/1/cards/{cardId}/actions/comments\` — body: \`{ text }\`
558
+ - Create list: \`POST https://api.trello.com/1/lists\` — body: \`{ name, idBoard }\`
559
+ - Update list: \`PUT https://api.trello.com/1/lists/{listId}\` — body: \`{ name }\``,
560
+ jira: `### Jira Cloud REST API v3
561
+
562
+ Base URL: \`https://{siteUrl}/rest/api/3/\` (use the site URL from your connection)
563
+
564
+ **Read operations:**
565
+ - \`GET /rest/api/3/myself\` — current user info
566
+ - \`GET /rest/api/3/search/jql?jql={query}\` — search issues with JQL (preferred)
567
+ - \`GET /rest/api/3/search?jql={query}\` — search issues (deprecated, use search/jql instead)
568
+ - \`GET /rest/api/3/issue/{issueIdOrKey}\` — get issue details
569
+ - \`GET /rest/api/3/issue/{issueIdOrKey}/comment\` — get issue comments
570
+ - \`GET /rest/api/3/project\` — list projects
571
+ - \`GET /rest/api/3/project/{projectIdOrKey}\` — get project details
572
+
573
+ **Write operations:**
574
+ - \`POST /rest/api/3/issue\` — create issue (body: \`{"fields":{"project":{"key":"PROJ"},"summary":"...","issuetype":{"name":"Task"}}}\`)
575
+ - \`PUT /rest/api/3/issue/{issueIdOrKey}\` — update issue fields
576
+ - \`POST /rest/api/3/issue/{issueIdOrKey}/comment\` — add comment (body: \`{"body":{"type":"doc","version":1,"content":[{"type":"paragraph","content":[{"type":"text","text":"..."}]}]}}\`)
577
+ - \`POST /rest/api/3/issue/{issueIdOrKey}/transitions\` — transition issue (body: \`{"transition":{"id":"..."}}\`)
578
+ - \`DELETE /rest/api/3/issue/{issueIdOrKey}\` — delete issue
579
+
580
+ > Do NOT include Authorization headers; the vault injects Basic auth credentials automatically.
581
+ > Always use the full URL including your Jira site domain.`,
582
+ };
583
+ // ---------------------------------------------------------------------------
584
+ // Chunked file writing
585
+ // ---------------------------------------------------------------------------
586
+ /**
587
+ * Write chunked reference files to a state directory.
588
+ *
589
+ * - Base file (vault-reference.md): tools, permission flow, action templates, notes
590
+ * - Per-service files (vault-ref-{service}.md): API docs for each provider
591
+ *
592
+ * Returns the paths so the system prompt can list them.
593
+ */
594
+ export function writeReferenceFiles(stateDir, logger) {
595
+ // 1. Write base reference (no provider API docs)
596
+ const basePath = join(stateDir, "vault-reference.md");
597
+ const baseContent = [
598
+ SECTION_HEADER,
599
+ SECTION_TOOLS,
600
+ SECTION_PERMISSION_FLOW,
601
+ SECTION_ACTION_TEMPLATES,
602
+ SECTION_NOTES,
603
+ ].join("\n\n");
604
+ writeFileSync(basePath, baseContent, "utf-8");
605
+ logger?.info?.(`wrote base reference to ${basePath} (${baseContent.length} chars)`);
606
+ // 2. Write ALL per-service API reference files.
607
+ const serviceFiles = [];
608
+ const seen = new Set();
609
+ for (const [provider, section] of Object.entries(CHUNKED_API_SECTIONS)) {
610
+ if (!section || seen.has(section)) {
611
+ continue;
612
+ }
613
+ seen.add(section);
614
+ const serviceName = PROVIDER_TO_SERVICE[provider] ?? provider;
615
+ const filePath = join(stateDir, `vault-ref-${serviceName}.md`);
616
+ writeFileSync(filePath, section, "utf-8");
617
+ serviceFiles.push(filePath);
618
+ logger?.info?.(`wrote service reference to ${filePath} (${section.length} chars)`);
619
+ }
620
+ return { basePath, serviceFiles };
621
+ }
622
+ /**
623
+ * Build the lean system prompt that points to the chunked reference files.
624
+ * Injected via the before_agent_start / before_prompt_build hook.
625
+ */
626
+ export function buildChunkedPrompt(basePath, serviceFiles) {
627
+ const instructions = [
628
+ `You have AgentHiFive vault integration for external services (email, calendar, files, messaging).`,
629
+ `For vault-managed services, ALWAYS use vault_execute — never the built-in message tool (it will 404). Never ask for credentials or tokens directly.`,
630
+ `Base reference (tools, permissions, action templates): ${basePath}`,
631
+ ...(serviceFiles.length > 0
632
+ ? [
633
+ `Service API docs (read the relevant file before making vault_execute calls):`,
634
+ ...serviceFiles.map((f) => ` - ${f}`),
635
+ ]
636
+ : []),
637
+ `When asked about available integrations, supported services, or new connections: read the vault reference file FIRST (action templates table) to see what's supported — before calling vault_connections_list. The reference is the source of truth for supported services; connections only show what's currently active.`,
638
+ `CRITICAL — Enabling/disabling vault-managed channels: ALWAYS use config.patch, NOT request_permission.`,
639
+ `NEVER ask users for bot tokens, app tokens, or API keys — the vault injects credentials automatically.`,
640
+ `If the vault returns a "hint" field in an error response, follow its guidance.`,
641
+ `Step-up approvals: when vault_execute returns 202 (approval required), tell the user their request needs approval and to check the dashboard. Do NOT call vault_await_approval — the system will automatically notify you when the approval resolves. When you receive an approval notification, re-submit the exact same vault_execute call with the approvalId provided.`,
642
+ ].join("\n");
643
+ return `<vault-context silent="true">\n${instructions}\n</vault-context>`;
644
+ }
645
+ //# sourceMappingURL=prompt-reference.js.map