@hailer/mcp 1.0.28 → 1.1.2

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 (233) hide show
  1. package/.claude/.session-checked +1 -0
  2. package/.claude/agents/agent-ada-skill-builder.md +10 -2
  3. package/.claude/agents/agent-alejandro-function-fields.md +104 -37
  4. package/.claude/agents/agent-bjorn-config-audit.md +41 -21
  5. package/.claude/agents/agent-builder-agent-creator.md +13 -3
  6. package/.claude/agents/agent-code-simplifier.md +53 -0
  7. package/.claude/agents/agent-dmitri-activity-crud.md +126 -11
  8. package/.claude/agents/agent-giuseppe-app-builder.md +212 -22
  9. package/.claude/agents/agent-gunther-mcp-tools.md +7 -36
  10. package/.claude/agents/agent-helga-workflow-config.md +75 -10
  11. package/.claude/agents/agent-igor-activity-mover-automation.md +125 -0
  12. package/.claude/agents/agent-ingrid-doc-templates.md +164 -36
  13. package/.claude/agents/agent-ivan-monolith.md +154 -0
  14. package/.claude/agents/agent-kenji-data-reader.md +15 -8
  15. package/.claude/agents/agent-lars-code-inspector.md +56 -8
  16. package/.claude/agents/agent-marco-mockup-builder.md +110 -0
  17. package/.claude/agents/agent-marcus-api-documenter.md +323 -0
  18. package/.claude/agents/agent-marketplace-publisher.md +232 -72
  19. package/.claude/agents/agent-marketplace-reviewer.md +255 -79
  20. package/.claude/agents/agent-permissions-handler.md +208 -0
  21. package/.claude/agents/agent-simple-writer.md +48 -0
  22. package/.claude/agents/agent-svetlana-code-review.md +127 -14
  23. package/.claude/agents/agent-tanya-test-runner.md +333 -0
  24. package/.claude/agents/agent-ui-designer.md +100 -0
  25. package/.claude/agents/agent-viktor-sql-insights.md +19 -6
  26. package/.claude/agents/agent-web-search.md +55 -0
  27. package/.claude/agents/agent-yevgeni-discussions.md +7 -1
  28. package/.claude/agents/agent-zara-zapier.md +159 -0
  29. package/.claude/commands/app-squad.md +135 -0
  30. package/.claude/commands/audit-squad.md +158 -0
  31. package/.claude/commands/autoplan.md +563 -0
  32. package/.claude/commands/cleanup-squad.md +98 -0
  33. package/.claude/commands/config-squad.md +106 -0
  34. package/.claude/commands/crud-squad.md +87 -0
  35. package/.claude/commands/data-squad.md +97 -0
  36. package/.claude/commands/debug-squad.md +303 -0
  37. package/.claude/commands/doc-squad.md +65 -0
  38. package/.claude/commands/handoff.md +137 -0
  39. package/.claude/commands/health.md +49 -0
  40. package/.claude/commands/help.md +2 -1
  41. package/.claude/commands/help:agents.md +96 -16
  42. package/.claude/commands/help:commands.md +55 -11
  43. package/.claude/commands/help:faq.md +16 -1
  44. package/.claude/commands/help:skills.md +93 -0
  45. package/.claude/commands/hotfix-squad.md +112 -0
  46. package/.claude/commands/integration-squad.md +82 -0
  47. package/.claude/commands/janitor-squad.md +167 -0
  48. package/.claude/commands/learn-auto.md +120 -0
  49. package/.claude/commands/learn.md +120 -0
  50. package/.claude/commands/mcp-list.md +27 -0
  51. package/.claude/commands/onboard-squad.md +140 -0
  52. package/.claude/commands/plan-workspace.md +732 -0
  53. package/.claude/commands/prd.md +131 -0
  54. package/.claude/commands/project-status.md +82 -0
  55. package/.claude/commands/publish.md +138 -0
  56. package/.claude/commands/recap.md +69 -0
  57. package/.claude/commands/restore.md +64 -0
  58. package/.claude/commands/review-squad.md +152 -0
  59. package/.claude/commands/save.md +24 -0
  60. package/.claude/commands/stats.md +19 -0
  61. package/.claude/commands/swarm.md +210 -0
  62. package/.claude/commands/tool-builder.md +3 -1
  63. package/.claude/commands/ws-pull.md +1 -1
  64. package/.claude/commands/yolo-off.md +17 -0
  65. package/.claude/commands/yolo.md +82 -0
  66. package/.claude/hooks/_shared-memory.cjs +305 -0
  67. package/.claude/hooks/_utils.cjs +134 -0
  68. package/.claude/hooks/agent-failure-detector.cjs +164 -79
  69. package/.claude/hooks/agent-usage-logger.cjs +204 -0
  70. package/.claude/hooks/app-edit-guard.cjs +20 -4
  71. package/.claude/hooks/auto-learn.cjs +316 -0
  72. package/.claude/hooks/bash-guard.cjs +282 -0
  73. package/.claude/hooks/builder-mode-manager.cjs +183 -54
  74. package/.claude/hooks/bulk-activity-guard.cjs +283 -0
  75. package/.claude/hooks/context-watchdog.cjs +292 -0
  76. package/.claude/hooks/delegation-reminder.cjs +478 -0
  77. package/.claude/hooks/design-system-lint.cjs +283 -0
  78. package/.claude/hooks/post-scaffold-hook.cjs +16 -3
  79. package/.claude/hooks/prompt-guard.cjs +366 -0
  80. package/.claude/hooks/publish-template-guard.cjs +16 -0
  81. package/.claude/hooks/session-start.cjs +35 -0
  82. package/.claude/hooks/shared-memory-writer.cjs +147 -0
  83. package/.claude/hooks/skill-injector.cjs +140 -0
  84. package/.claude/hooks/skill-usage-logger.cjs +258 -0
  85. package/.claude/hooks/src-edit-guard.cjs +16 -1
  86. package/.claude/hooks/sync-marketplace-agents.cjs +53 -8
  87. package/.claude/scripts/yolo-toggle.cjs +142 -0
  88. package/.claude/settings.json +141 -14
  89. package/.claude/skills/SDK-activity-patterns/SKILL.md +428 -0
  90. package/.claude/skills/SDK-document-templates/SKILL.md +1033 -0
  91. package/.claude/skills/SDK-function-fields/SKILL.md +542 -0
  92. package/.claude/skills/SDK-generate-skill/SKILL.md +92 -0
  93. package/.claude/skills/SDK-init-skill/SKILL.md +127 -0
  94. package/.claude/skills/SDK-insight-queries/SKILL.md +787 -0
  95. package/.claude/skills/SDK-ws-config-skill/SKILL.md +1139 -0
  96. package/.claude/skills/agent-structure/SKILL.md +98 -0
  97. package/.claude/skills/api-documentation-patterns/SKILL.md +474 -0
  98. package/.claude/skills/chrome-mcp-reference/SKILL.md +370 -0
  99. package/.claude/skills/delegation-routing/SKILL.md +202 -0
  100. package/.claude/skills/frontend-design/SKILL.md +254 -0
  101. package/.claude/skills/hailer-activity-mover/SKILL.md +213 -0
  102. package/.claude/skills/hailer-api-client/SKILL.md +518 -0
  103. package/.claude/skills/hailer-app-builder/SKILL.md +939 -11
  104. package/.claude/skills/hailer-apps-pictures/SKILL.md +269 -0
  105. package/.claude/skills/hailer-design-system/SKILL.md +235 -0
  106. package/.claude/skills/hailer-monolith-automations/SKILL.md +686 -0
  107. package/.claude/skills/hailer-permissions-system/SKILL.md +121 -0
  108. package/.claude/skills/hailer-project-protocol/SKILL.md +488 -0
  109. package/.claude/skills/hailer-rest-api/SKILL.md +61 -0
  110. package/.claude/skills/hailer-rest-api/hailer-activities.md +184 -0
  111. package/.claude/skills/hailer-rest-api/hailer-admin.md +473 -0
  112. package/.claude/skills/hailer-rest-api/hailer-calendar.md +256 -0
  113. package/.claude/skills/hailer-rest-api/hailer-feed.md +249 -0
  114. package/.claude/skills/hailer-rest-api/hailer-insights.md +195 -0
  115. package/.claude/skills/hailer-rest-api/hailer-messaging.md +276 -0
  116. package/.claude/skills/hailer-rest-api/hailer-workflows.md +283 -0
  117. package/.claude/skills/insight-join-patterns/SKILL.md +3 -0
  118. package/.claude/skills/integration-patterns/SKILL.md +421 -0
  119. package/.claude/skills/json-only-output/SKILL.md +52 -12
  120. package/.claude/skills/lsp-setup/SKILL.md +160 -0
  121. package/.claude/skills/mcp-direct-tools/SKILL.md +153 -0
  122. package/.claude/skills/optional-parameters/SKILL.md +32 -23
  123. package/.claude/skills/publish-hailer-app/SKILL.md +76 -12
  124. package/.claude/skills/testing-patterns/SKILL.md +630 -0
  125. package/.claude/skills/tool-builder/SKILL.md +250 -0
  126. package/.claude/skills/tool-parameter-usage/SKILL.md +59 -45
  127. package/.claude/skills/tool-response-verification/SKILL.md +82 -48
  128. package/.claude/skills/zapier-hailer-patterns/SKILL.md +581 -0
  129. package/.env.example +26 -7
  130. package/CLAUDE.md +290 -224
  131. package/dist/CLAUDE.md +370 -0
  132. package/dist/app.d.ts +1 -1
  133. package/dist/app.js +101 -101
  134. package/dist/bot/bot-config.d.ts +26 -0
  135. package/dist/bot/bot-config.js +135 -0
  136. package/dist/bot/bot-manager.d.ts +40 -0
  137. package/dist/bot/bot-manager.js +137 -0
  138. package/dist/bot/bot.d.ts +127 -0
  139. package/dist/bot/bot.js +1328 -0
  140. package/dist/bot/operation-logger.d.ts +28 -0
  141. package/dist/bot/operation-logger.js +132 -0
  142. package/dist/bot/services/conversation-manager.d.ts +60 -0
  143. package/dist/bot/services/conversation-manager.js +246 -0
  144. package/dist/bot/services/index.d.ts +9 -0
  145. package/dist/bot/services/index.js +18 -0
  146. package/dist/bot/services/message-classifier.d.ts +42 -0
  147. package/dist/bot/services/message-classifier.js +228 -0
  148. package/dist/bot/services/message-formatter.d.ts +88 -0
  149. package/dist/bot/services/message-formatter.js +411 -0
  150. package/dist/bot/services/session-logger.d.ts +162 -0
  151. package/dist/bot/services/session-logger.js +724 -0
  152. package/dist/bot/services/token-billing.d.ts +78 -0
  153. package/dist/bot/services/token-billing.js +233 -0
  154. package/dist/bot/services/types.d.ts +169 -0
  155. package/dist/bot/services/types.js +12 -0
  156. package/dist/bot/services/typing-indicator.d.ts +23 -0
  157. package/dist/bot/services/typing-indicator.js +60 -0
  158. package/dist/bot/services/workspace-schema-cache.d.ts +122 -0
  159. package/dist/bot/services/workspace-schema-cache.js +506 -0
  160. package/dist/bot/tool-executor.d.ts +28 -0
  161. package/dist/bot/tool-executor.js +48 -0
  162. package/dist/bot/workspace-overview.d.ts +12 -0
  163. package/dist/bot/workspace-overview.js +94 -0
  164. package/dist/cli.d.ts +1 -8
  165. package/dist/cli.js +1 -249
  166. package/dist/config.d.ts +96 -3
  167. package/dist/config.js +148 -37
  168. package/dist/core.d.ts +5 -0
  169. package/dist/core.js +61 -8
  170. package/dist/lib/discussion-lock.d.ts +42 -0
  171. package/dist/lib/discussion-lock.js +110 -0
  172. package/dist/lib/logger.d.ts +0 -1
  173. package/dist/lib/logger.js +39 -23
  174. package/dist/lib/request-logger.d.ts +77 -0
  175. package/dist/lib/request-logger.js +147 -0
  176. package/dist/mcp/UserContextCache.js +16 -13
  177. package/dist/mcp/hailer-clients.js +18 -17
  178. package/dist/mcp/signal-handler.js +29 -13
  179. package/dist/mcp/tool-registry.d.ts +4 -15
  180. package/dist/mcp/tool-registry.js +94 -32
  181. package/dist/mcp/tools/activity.js +28 -69
  182. package/dist/mcp/tools/app-core.js +9 -4
  183. package/dist/mcp/tools/app-marketplace.js +22 -12
  184. package/dist/mcp/tools/app-member.js +5 -2
  185. package/dist/mcp/tools/app-scaffold.js +32 -18
  186. package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
  187. package/dist/mcp/tools/bot-config/constants.js +94 -0
  188. package/dist/mcp/tools/bot-config/core.d.ts +253 -0
  189. package/dist/mcp/tools/bot-config/core.js +2456 -0
  190. package/dist/mcp/tools/bot-config/index.d.ts +10 -0
  191. package/dist/mcp/tools/bot-config/index.js +59 -0
  192. package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
  193. package/dist/mcp/tools/bot-config/tools.js +15 -0
  194. package/dist/mcp/tools/bot-config/types.d.ts +50 -0
  195. package/dist/mcp/tools/bot-config/types.js +6 -0
  196. package/dist/mcp/tools/discussion.js +107 -77
  197. package/dist/mcp/tools/document.d.ts +11 -0
  198. package/dist/mcp/tools/document.js +741 -0
  199. package/dist/mcp/tools/file.js +5 -2
  200. package/dist/mcp/tools/insight.js +36 -12
  201. package/dist/mcp/tools/investigate.d.ts +9 -0
  202. package/dist/mcp/tools/investigate.js +254 -0
  203. package/dist/mcp/tools/user.d.ts +2 -4
  204. package/dist/mcp/tools/user.js +9 -50
  205. package/dist/mcp/tools/workflow.d.ts +1 -0
  206. package/dist/mcp/tools/workflow.js +164 -52
  207. package/dist/mcp/utils/hailer-api-client.js +26 -17
  208. package/dist/mcp/webhook-handler.d.ts +64 -3
  209. package/dist/mcp/webhook-handler.js +219 -9
  210. package/dist/mcp-server.d.ts +4 -0
  211. package/dist/mcp-server.js +237 -25
  212. package/dist/plugins/bug-fixer/index.d.ts +2 -0
  213. package/dist/plugins/bug-fixer/index.js +18 -0
  214. package/dist/plugins/bug-fixer/tools.d.ts +45 -0
  215. package/dist/plugins/bug-fixer/tools.js +1096 -0
  216. package/package.json +10 -10
  217. package/scripts/test-hal-tools.ts +154 -0
  218. package/.claude/agents/agent-nora-name-functions.md +0 -123
  219. package/.claude/assistant-knowledge.md +0 -23
  220. package/.claude/commands/install-plugin.md +0 -261
  221. package/.claude/commands/list-plugins.md +0 -42
  222. package/.claude/commands/marketplace-setup.md +0 -33
  223. package/.claude/commands/publish-plugin.md +0 -55
  224. package/.claude/commands/uninstall-plugin.md +0 -87
  225. package/.claude/hooks/interactive-mode.cjs +0 -87
  226. package/.claude/hooks/mcp-server-guard.cjs +0 -108
  227. package/.claude/skills/marketplace-publishing.md +0 -155
  228. package/dist/bot/chat-bot.d.ts +0 -31
  229. package/dist/bot/chat-bot.js +0 -357
  230. package/dist/mcp/tools/metrics.d.ts +0 -13
  231. package/dist/mcp/tools/metrics.js +0 -546
  232. package/dist/stdio-server.d.ts +0 -14
  233. package/dist/stdio-server.js +0 -114
@@ -0,0 +1,741 @@
1
+ "use strict";
2
+ /**
3
+ * Document Tools - PDF/CSV Generation
4
+ *
5
+ * Tools for generating documents from templates:
6
+ * - List document templates (READ)
7
+ * - Generate PDF documents from activities (WRITE)
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.generateDocumentTool = exports.listDocumentTemplatesTool = void 0;
44
+ const zod_1 = require("zod");
45
+ const tool_registry_1 = require("../tool-registry");
46
+ const index_1 = require("../utils/index");
47
+ const request_logger_1 = require("../../lib/request-logger");
48
+ const fs = __importStar(require("fs"));
49
+ const path = __importStar(require("path"));
50
+ const os = __importStar(require("os"));
51
+ const logger = (0, index_1.createLogger)({ component: 'document-tools' });
52
+ // Helper: extract error message from various error types
53
+ function extractErrorMessage(error) {
54
+ if (error instanceof Error) {
55
+ return error.message;
56
+ }
57
+ else if (error && typeof error === 'object') {
58
+ if ('message' in error)
59
+ return String(error.message);
60
+ if ('error' in error) {
61
+ const e = error.error;
62
+ return typeof e === 'string' ? e : JSON.stringify(e);
63
+ }
64
+ try {
65
+ return JSON.stringify(error);
66
+ }
67
+ catch {
68
+ return '[Complex error object]';
69
+ }
70
+ }
71
+ return String(error);
72
+ }
73
+ function convertFiltersToApiFormat(mcpFilters) {
74
+ const apiFilters = { and: [] };
75
+ for (const [fieldId, filter] of Object.entries(mcpFilters)) {
76
+ switch (filter.operator) {
77
+ case 'text_search':
78
+ if (filter.value !== undefined) {
79
+ apiFilters.and.push({ [fieldId]: { textSearch: filter.value } });
80
+ }
81
+ break;
82
+ case 'equals':
83
+ if (filter.value !== undefined) {
84
+ apiFilters.and.push({ [fieldId]: { equalTo: filter.value } });
85
+ }
86
+ break;
87
+ case 'not_equals':
88
+ if (filter.value !== undefined) {
89
+ apiFilters.and.push({ [fieldId]: { notEqualTo: filter.value } });
90
+ }
91
+ break;
92
+ case 'contains':
93
+ if (filter.value !== undefined) {
94
+ apiFilters.and.push({ [fieldId]: { contains: filter.value } });
95
+ }
96
+ break;
97
+ case 'greater_than':
98
+ if (filter.value !== undefined && typeof filter.value === 'number') {
99
+ apiFilters.and.push({ [fieldId]: { greaterThan: filter.value } });
100
+ }
101
+ break;
102
+ case 'less_than':
103
+ if (filter.value !== undefined && typeof filter.value === 'number') {
104
+ apiFilters.and.push({ [fieldId]: { lessThan: filter.value } });
105
+ }
106
+ break;
107
+ case 'range':
108
+ if (filter.start !== undefined && filter.end !== undefined) {
109
+ apiFilters.and.push({ [fieldId]: { between: [String(filter.start), String(filter.end)] } });
110
+ }
111
+ break;
112
+ default:
113
+ // Unknown operator, try text_search as fallback
114
+ if (filter.value !== undefined) {
115
+ apiFilters.and.push({ [fieldId]: { textSearch: filter.value } });
116
+ }
117
+ }
118
+ }
119
+ return apiFilters;
120
+ }
121
+ // Helper: delay for rate limiting and retry backoff
122
+ const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
123
+ // Retry configuration
124
+ const RETRY_CONFIG = {
125
+ maxRetries: 3,
126
+ baseDelayMs: 1000, // 1 second base delay
127
+ batchDelayMs: 500, // 500ms between batches
128
+ retryableCodes: [502, 503, 504, 429] // Gateway errors and rate limits
129
+ };
130
+ // ============================================================================
131
+ // TOOL 1: LIST DOCUMENT TEMPLATES
132
+ // ============================================================================
133
+ const listDocumentTemplatesDescription = `List available document templates for PDF/CSV generation
134
+
135
+ **Returns**: Template IDs, names, types, and associated workflow IDs
136
+
137
+ **Example**:
138
+ \`\`\`javascript
139
+ list_document_templates({ workflowId: "691ffdf84217e9e8434e5694" })
140
+ \`\`\``;
141
+ exports.listDocumentTemplatesTool = {
142
+ name: 'list_document_templates',
143
+ group: tool_registry_1.ToolGroup.READ,
144
+ description: listDocumentTemplatesDescription,
145
+ schema: zod_1.z.object({
146
+ workflowId: zod_1.z
147
+ .string()
148
+ .optional()
149
+ .describe("Optional workflow ID to filter templates for a specific workflow")
150
+ }),
151
+ async execute(args, context) {
152
+ logger.debug('Listing document templates', {
153
+ workflowId: args.workflowId,
154
+ apiKey: context.apiKey.substring(0, 8) + '...'
155
+ });
156
+ try {
157
+ const rawInit = context.init;
158
+ let templates = [];
159
+ // Method 1: Check workspace-level templates
160
+ if (rawInit.templates) {
161
+ const workspaceTemplates = Array.isArray(rawInit.templates)
162
+ ? rawInit.templates
163
+ : Object.values(rawInit.templates);
164
+ templates.push(...workspaceTemplates);
165
+ }
166
+ if (rawInit.documentTemplates) {
167
+ const docTemplates = Array.isArray(rawInit.documentTemplates)
168
+ ? rawInit.documentTemplates
169
+ : Object.values(rawInit.documentTemplates);
170
+ templates.push(...docTemplates);
171
+ }
172
+ // Method 2: Check per-workflow templates (in processes)
173
+ if (rawInit.processes) {
174
+ const processes = Array.isArray(rawInit.processes)
175
+ ? rawInit.processes
176
+ : Object.values(rawInit.processes);
177
+ for (const process of processes) {
178
+ // Check templates in process
179
+ if (process.templates) {
180
+ const procTemplates = Array.isArray(process.templates)
181
+ ? process.templates
182
+ : Object.values(process.templates);
183
+ // Add workflowId to each template
184
+ for (const t of procTemplates) {
185
+ templates.push({ ...t, workflowId: process._id, workflowName: process.name });
186
+ }
187
+ }
188
+ // Check documentTemplates in process
189
+ if (process.documentTemplates) {
190
+ const procDocTemplates = Array.isArray(process.documentTemplates)
191
+ ? process.documentTemplates
192
+ : Object.values(process.documentTemplates);
193
+ for (const t of procDocTemplates) {
194
+ templates.push({ ...t, workflowId: process._id, workflowName: process.name });
195
+ }
196
+ }
197
+ // Check printTemplates in process
198
+ if (process.printTemplates) {
199
+ const printTemplates = Array.isArray(process.printTemplates)
200
+ ? process.printTemplates
201
+ : Object.values(process.printTemplates);
202
+ for (const t of printTemplates) {
203
+ templates.push({ ...t, workflowId: process._id, workflowName: process.name });
204
+ }
205
+ }
206
+ }
207
+ }
208
+ // Method 3: Try dedicated API call if no templates found
209
+ if (templates.length === 0) {
210
+ try {
211
+ // Try v3.template.list or similar
212
+ const apiTemplates = await context.hailer.request('v3.documentTemplate.list', []);
213
+ if (Array.isArray(apiTemplates)) {
214
+ templates.push(...apiTemplates);
215
+ }
216
+ else if (apiTemplates?.templates) {
217
+ templates.push(...apiTemplates.templates);
218
+ }
219
+ }
220
+ catch (apiError) {
221
+ logger.debug('v3.documentTemplate.list not available', { error: apiError });
222
+ }
223
+ }
224
+ logger.debug('Template discovery results', {
225
+ initKeys: Object.keys(rawInit),
226
+ processCount: rawInit.processes ? Object.keys(rawInit.processes).length : 0,
227
+ templatesFound: templates.length
228
+ });
229
+ // Filter by workflow if provided
230
+ if (args.workflowId) {
231
+ templates = templates.filter((t) => t.workflowId === args.workflowId ||
232
+ t.processId === args.workflowId ||
233
+ t.pid === args.workflowId);
234
+ }
235
+ if (templates.length === 0) {
236
+ // Return diagnostic info to help debug
237
+ const processInfo = rawInit.processes
238
+ ? Object.entries(rawInit.processes).slice(0, 3).map(([id, p]) => ({
239
+ id,
240
+ name: p.name,
241
+ hasTemplates: !!p.templates,
242
+ hasDocumentTemplates: !!p.documentTemplates,
243
+ hasPrintTemplates: !!p.printTemplates,
244
+ keys: Object.keys(p).filter(k => k.toLowerCase().includes('template'))
245
+ }))
246
+ : [];
247
+ return {
248
+ content: [{
249
+ type: "text",
250
+ text: `šŸ“„ No document templates found${args.workflowId ? ` for workflow ${args.workflowId}` : ''}.\n\n` +
251
+ `**Init data keys**: ${Object.keys(rawInit).join(', ')}\n\n` +
252
+ `**Process sample** (first 3):\n${JSON.stringify(processInfo, null, 2)}\n\n` +
253
+ `šŸ’” Document templates may need to be created in Hailer UI first.`
254
+ }]
255
+ };
256
+ }
257
+ let responseText = `šŸ“„ **Document Templates** (${templates.length} total):\n\n`;
258
+ templates.forEach((template, index) => {
259
+ // Get template ID from various possible fields
260
+ const templateId = template._id || template.id || template.templateId || template.key || Object.keys(template)[0];
261
+ const templateName = template.name || template.label || template.title || 'Untitled';
262
+ responseText += `${index + 1}. **${templateName}**\n`;
263
+ responseText += ` - Template ID: \`${templateId}\`\n`;
264
+ if (template.type)
265
+ responseText += ` - Type: ${template.type}\n`;
266
+ if (template.workflowId || template.processId) {
267
+ responseText += ` - Workflow: ${template.workflowName || ''} (\`${template.workflowId || template.processId}\`)\n`;
268
+ }
269
+ responseText += `\n`;
270
+ });
271
+ responseText += `\nšŸ’” **Usage**: Use \`generate_document\` with a template ID and activity IDs to create PDF documents.`;
272
+ return {
273
+ content: [{
274
+ type: "text",
275
+ text: responseText
276
+ }]
277
+ };
278
+ }
279
+ catch (error) {
280
+ if (!request_logger_1.RequestLogger.getCurrent())
281
+ logger.error("Failed to list document templates", error);
282
+ return {
283
+ content: [{
284
+ type: "text",
285
+ text: `āŒ Failed to list document templates: ${error instanceof Error ? error.message : String(error)}\n\n` +
286
+ `šŸ’” Document templates may not be available in this workspace.`
287
+ }]
288
+ };
289
+ }
290
+ }
291
+ };
292
+ // ============================================================================
293
+ // TOOL 2: GENERATE DOCUMENT
294
+ // ============================================================================
295
+ const generateDocumentDescription = `Generate PDF document from template and activities, upload to Hailer
296
+
297
+ **Two modes:**
298
+ 1. **Direct IDs**: Pass \`activityIds\` array directly
299
+ 2. **Filter-based**: Pass \`workflowId\` + optional filters (fetches IDs internally - saves context!)
300
+
301
+ **Auto-batching**: For large activity lists (>50), automatically splits into batches to avoid timeouts.
302
+
303
+ **Parameters**:
304
+ - \`templateId\`: Document template ID (from list_document_templates)
305
+ - \`activityIds\`: Array of activity IDs (OR use workflowId + filters)
306
+ - \`workflowId\`: Workflow to fetch activities from (alternative to activityIds)
307
+ - \`phaseId\`: Optional phase filter (used with workflowId)
308
+ - \`filters\`: Optional field filters, same format as list_activities
309
+ - \`limit\`: Max activities to process (default: 1000)
310
+ - \`discussionId\`: Optional - post PDF(s) to this discussion
311
+ - \`batchSize\`: Optional - activities per PDF (default: 50)
312
+
313
+ **Example with filters (recommended for large datasets):**
314
+ \`\`\`javascript
315
+ generate_document({
316
+ templateId: "abc123",
317
+ workflowId: "workflow-id",
318
+ phaseId: "phase-id",
319
+ filters: { "fieldId": { "operator": "equals", "value": "Defender" }},
320
+ discussionId: "discussion-id"
321
+ })
322
+ \`\`\`
323
+
324
+ **Example with direct IDs:**
325
+ \`\`\`javascript
326
+ generate_document({
327
+ templateId: "abc123",
328
+ activityIds: ["id1", "id2"],
329
+ discussionId: "discussion-id"
330
+ })
331
+ \`\`\``;
332
+ exports.generateDocumentTool = {
333
+ name: 'generate_document',
334
+ group: tool_registry_1.ToolGroup.WRITE,
335
+ description: generateDocumentDescription,
336
+ schema: zod_1.z.object({
337
+ templateId: zod_1.z
338
+ .string()
339
+ .min(1, "Template ID is required")
340
+ .describe("Document template ID (use list_document_templates to find available templates)"),
341
+ // Option 1: Direct activity IDs
342
+ activityIds: zod_1.z
343
+ .preprocess((val) => {
344
+ if (typeof val === 'string') {
345
+ try {
346
+ return JSON.parse(val);
347
+ }
348
+ catch {
349
+ return val.split(',').map(id => id.trim()).filter(Boolean);
350
+ }
351
+ }
352
+ return val;
353
+ }, zod_1.z.array(zod_1.z.string()))
354
+ .optional()
355
+ .describe("Array of activity IDs (OR use workflowId + filters instead)"),
356
+ // Option 2: Filter-based (fetches IDs internally)
357
+ workflowId: zod_1.z
358
+ .string()
359
+ .optional()
360
+ .describe("Workflow ID to fetch activities from (alternative to activityIds)"),
361
+ phaseId: zod_1.z
362
+ .string()
363
+ .optional()
364
+ .describe("Optional phase ID filter (used with workflowId)"),
365
+ filters: zod_1.z
366
+ .any()
367
+ .optional()
368
+ .describe("Field filters, same format as list_activities: { fieldId: { operator, value }}"),
369
+ limit: zod_1.z
370
+ .number()
371
+ .optional()
372
+ .default(1000)
373
+ .describe("Max activities to fetch when using workflowId (default: 1000)"),
374
+ // Common options
375
+ discussionId: zod_1.z
376
+ .string()
377
+ .optional()
378
+ .describe("Optional discussion ID - if provided, the PDF(s) will be posted to this discussion"),
379
+ timeZone: zod_1.z
380
+ .string()
381
+ .optional()
382
+ .default("Europe/Helsinki")
383
+ .describe("Timezone for date formatting in document (default: Europe/Helsinki)"),
384
+ batchSize: zod_1.z
385
+ .number()
386
+ .optional()
387
+ .default(50)
388
+ .describe("Activities per PDF batch (default: 50). Large lists are auto-batched to avoid timeouts.")
389
+ }),
390
+ async execute(args, context) {
391
+ const batchSize = args.batchSize || 50;
392
+ const limit = args.limit || 1000;
393
+ let activityIds = args.activityIds || [];
394
+ // Validate: need either activityIds or workflowId
395
+ if (activityIds.length === 0 && !args.workflowId) {
396
+ return {
397
+ content: [{
398
+ type: "text",
399
+ text: `āŒ Missing required parameter: provide either \`activityIds\` or \`workflowId\`.\n\n` +
400
+ `**Option 1 - Direct IDs:**\n` +
401
+ `\`\`\`json\n{ "templateId": "...", "activityIds": ["id1", "id2"] }\n\`\`\`\n\n` +
402
+ `**Option 2 - Filter-based (recommended for large datasets):**\n` +
403
+ `\`\`\`json\n{ "templateId": "...", "workflowId": "...", "phaseId": "...", "filters": {...} }\n\`\`\``
404
+ }]
405
+ };
406
+ }
407
+ // If workflowId provided, fetch activity IDs internally
408
+ if (args.workflowId && activityIds.length === 0) {
409
+ logger.debug('Fetching activities via filters', {
410
+ workflowId: args.workflowId,
411
+ phaseId: args.phaseId,
412
+ hasFilters: !!args.filters,
413
+ limit
414
+ });
415
+ try {
416
+ // Get phases if phaseId not provided - we need at least one phase
417
+ let phaseIds = [];
418
+ if (args.phaseId) {
419
+ phaseIds = [args.phaseId];
420
+ }
421
+ else {
422
+ // Get all phases for this workflow
423
+ const workflow = context.init.processes.find((p) => p._id === args.workflowId);
424
+ if (workflow?.phases) {
425
+ phaseIds = Object.keys(workflow.phases);
426
+ }
427
+ if (phaseIds.length === 0) {
428
+ throw new Error(`No phases found for workflow ${args.workflowId}`);
429
+ }
430
+ }
431
+ // Convert filters to API format if provided
432
+ let apiFilters;
433
+ if (args.filters && typeof args.filters === 'object') {
434
+ apiFilters = convertFiltersToApiFormat(args.filters);
435
+ logger.debug('Converted filters to API format', {
436
+ original: args.filters,
437
+ converted: apiFilters
438
+ });
439
+ }
440
+ // Fetch activities from each phase with pagination
441
+ const allIds = [];
442
+ const PAGE_SIZE = 200; // Fetch in pages of 200
443
+ for (const phaseId of phaseIds) {
444
+ if (allIds.length >= limit)
445
+ break;
446
+ let page = 0;
447
+ let hasMore = true;
448
+ // Paginate through all results for this phase
449
+ while (hasMore && allIds.length < limit) {
450
+ const remaining = limit - allIds.length;
451
+ const result = await context.hailer.fetchActivityList(args.workflowId, phaseId, Math.min(remaining, PAGE_SIZE), {
452
+ page,
453
+ filters: apiFilters,
454
+ returnFlat: true,
455
+ includeStats: true
456
+ // Don't pass fields - API returns _id by default
457
+ });
458
+ // Extract IDs from result
459
+ const activities = result?.activities || result?.list || result?.data || [];
460
+ const metadata = result?.metadata || {};
461
+ for (const activity of activities) {
462
+ if (allIds.length >= limit)
463
+ break;
464
+ if (activity._id) {
465
+ allIds.push(activity._id);
466
+ }
467
+ }
468
+ logger.debug(`Fetched page ${page + 1} from phase ${phaseId}`, {
469
+ phaseId,
470
+ page: page + 1,
471
+ fetched: activities.length,
472
+ totalInPhase: metadata.totalCount,
473
+ totalSoFar: allIds.length
474
+ });
475
+ // Check if there are more pages
476
+ const totalInPhase = metadata.totalCount || 0;
477
+ const fetchedSoFarInPhase = (page + 1) * PAGE_SIZE;
478
+ hasMore = activities.length === PAGE_SIZE && fetchedSoFarInPhase < totalInPhase;
479
+ page++;
480
+ // Safety: max 50 pages (10,000 activities per phase)
481
+ if (page >= 50) {
482
+ logger.warn('Hit pagination safety limit', { phaseId, page });
483
+ break;
484
+ }
485
+ }
486
+ }
487
+ activityIds = allIds;
488
+ if (activityIds.length === 0) {
489
+ return {
490
+ content: [{
491
+ type: "text",
492
+ text: `šŸ“„ No activities found matching the filters.\n\n` +
493
+ `**Workflow:** ${args.workflowId}\n` +
494
+ `**Phase:** ${args.phaseId || 'all phases'}\n` +
495
+ `**Filters:** ${args.filters ? JSON.stringify(args.filters) : 'none'}\n\n` +
496
+ `šŸ’” Try adjusting your filters or check that activities exist in this workflow.`
497
+ }]
498
+ };
499
+ }
500
+ logger.debug('Activity IDs fetched via filters', {
501
+ count: activityIds.length,
502
+ limit,
503
+ phaseCount: phaseIds.length
504
+ });
505
+ }
506
+ catch (fetchError) {
507
+ const errorMsg = extractErrorMessage(fetchError);
508
+ logger.error('Failed to fetch activities via filters', { error: errorMsg });
509
+ return {
510
+ content: [{
511
+ type: "text",
512
+ text: `āŒ Failed to fetch activities: ${errorMsg}\n\n` +
513
+ `**Workflow:** ${args.workflowId}\n` +
514
+ `**Phase:** ${args.phaseId || 'all phases'}\n` +
515
+ `**Filters:** ${args.filters ? JSON.stringify(args.filters) : 'none'}\n\n` +
516
+ `šŸ’” Check that:\n` +
517
+ `- Workflow and phase IDs are correct\n` +
518
+ `- Filter field IDs exist in the workflow\n` +
519
+ `- Filter format is correct: { "fieldId": { "operator": "text_search", "value": "..." }}`
520
+ }]
521
+ };
522
+ }
523
+ }
524
+ const totalActivities = activityIds.length;
525
+ logger.debug('Generating document', {
526
+ templateId: args.templateId,
527
+ activityCount: totalActivities,
528
+ batchSize,
529
+ batchCount: Math.ceil(totalActivities / batchSize),
530
+ hasDiscussionId: !!args.discussionId,
531
+ timeZone: args.timeZone,
532
+ mode: args.workflowId ? 'filter-based' : 'direct-ids'
533
+ });
534
+ try {
535
+ // Split activities into batches
536
+ const batches = [];
537
+ for (let i = 0; i < activityIds.length; i += batchSize) {
538
+ batches.push(activityIds.slice(i, i + batchSize));
539
+ }
540
+ const results = [];
541
+ const errors = [];
542
+ // Process each batch with retry logic and delays
543
+ for (let batchNum = 0; batchNum < batches.length; batchNum++) {
544
+ const batch = batches[batchNum];
545
+ // Add delay between batches (except first one)
546
+ if (batchNum > 0) {
547
+ await delay(RETRY_CONFIG.batchDelayMs);
548
+ }
549
+ logger.debug(`Processing batch ${batchNum + 1}/${batches.length}`, {
550
+ batchNum: batchNum + 1,
551
+ activityCount: batch.length
552
+ });
553
+ let lastError = '';
554
+ let success = false;
555
+ // Retry loop for this batch
556
+ for (let attempt = 0; attempt < RETRY_CONFIG.maxRetries && !success; attempt++) {
557
+ if (attempt > 0) {
558
+ // Exponential backoff: 1s, 2s, 4s
559
+ const backoffMs = RETRY_CONFIG.baseDelayMs * Math.pow(2, attempt - 1);
560
+ logger.debug(`Retry attempt ${attempt + 1} for batch ${batchNum + 1} after ${backoffMs}ms`);
561
+ await delay(backoffMs);
562
+ }
563
+ try {
564
+ // Build templateMap for this batch
565
+ const templateMap = { [args.templateId]: batch };
566
+ const requestBody = {
567
+ templateMap,
568
+ timeZone: args.timeZone || "Europe/Helsinki",
569
+ save: 0
570
+ };
571
+ // Call REST API to generate PDF (binary response)
572
+ const pdfBuffer = await context.hailer.callRest({
573
+ operation: 'generate_document',
574
+ endpoint: '/documentgenerator',
575
+ method: 'POST',
576
+ body: requestBody,
577
+ timeout: 120000, // 2 minutes per batch
578
+ responseType: 'binary'
579
+ });
580
+ // Convert buffer to Buffer instance if needed
581
+ let buffer;
582
+ if (typeof pdfBuffer === 'string') {
583
+ buffer = Buffer.from(pdfBuffer, 'base64');
584
+ }
585
+ else if (Buffer.isBuffer(pdfBuffer)) {
586
+ buffer = pdfBuffer;
587
+ }
588
+ else if (pdfBuffer instanceof ArrayBuffer) {
589
+ buffer = Buffer.from(pdfBuffer);
590
+ }
591
+ else {
592
+ throw new Error('Unexpected PDF response format');
593
+ }
594
+ // Save to temporary file
595
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
596
+ const filename = batches.length > 1
597
+ ? `document-batch${batchNum + 1}-${timestamp}.pdf`
598
+ : `document-${timestamp}.pdf`;
599
+ const tempDir = os.tmpdir();
600
+ const tempPath = path.join(tempDir, filename);
601
+ fs.writeFileSync(tempPath, buffer);
602
+ // Upload PDF to Hailer (also with retry for upload failures)
603
+ let uploadResult;
604
+ for (let uploadAttempt = 0; uploadAttempt < RETRY_CONFIG.maxRetries; uploadAttempt++) {
605
+ if (uploadAttempt > 0) {
606
+ const backoffMs = RETRY_CONFIG.baseDelayMs * Math.pow(2, uploadAttempt - 1);
607
+ logger.debug(`Retry upload attempt ${uploadAttempt + 1} after ${backoffMs}ms`);
608
+ await delay(backoffMs);
609
+ }
610
+ uploadResult = await context.hailer.uploadFile({
611
+ path: tempPath,
612
+ filename
613
+ });
614
+ if (uploadResult.success)
615
+ break;
616
+ // Check if error is retryable
617
+ const isRetryable = RETRY_CONFIG.retryableCodes.some(code => uploadResult.error?.includes(String(code)));
618
+ if (!isRetryable)
619
+ break;
620
+ }
621
+ // Clean up temp file
622
+ try {
623
+ fs.unlinkSync(tempPath);
624
+ }
625
+ catch { /* ignore */ }
626
+ if (!uploadResult.success) {
627
+ throw new Error(`PDF upload failed: ${uploadResult.error}`);
628
+ }
629
+ results.push({
630
+ fileId: uploadResult.fileId || '',
631
+ sizeKB: uploadResult.sizeKB || '',
632
+ activityCount: batch.length,
633
+ batchNum: batchNum + 1
634
+ });
635
+ logger.debug(`Batch ${batchNum + 1} complete`, {
636
+ fileId: uploadResult.fileId,
637
+ sizeKB: uploadResult.sizeKB,
638
+ attempts: attempt + 1
639
+ });
640
+ success = true;
641
+ }
642
+ catch (batchError) {
643
+ lastError = batchError instanceof Error ? batchError.message : String(batchError);
644
+ // Check if error is retryable (502, 503, 504, 429)
645
+ const isRetryable = RETRY_CONFIG.retryableCodes.some(code => lastError.includes(String(code)));
646
+ if (!isRetryable) {
647
+ logger.debug(`Non-retryable error for batch ${batchNum + 1}: ${lastError}`);
648
+ break; // Don't retry non-retryable errors
649
+ }
650
+ logger.debug(`Retryable error for batch ${batchNum + 1} (attempt ${attempt + 1}): ${lastError}`);
651
+ }
652
+ }
653
+ if (!success) {
654
+ errors.push({ batchNum: batchNum + 1, error: lastError });
655
+ logger.error(`Batch ${batchNum + 1} failed after ${RETRY_CONFIG.maxRetries} attempts`, { error: lastError });
656
+ }
657
+ }
658
+ // Check if all batches failed
659
+ if (results.length === 0) {
660
+ throw new Error(`All ${batches.length} batches failed. First error: ${errors[0]?.error || 'Unknown'}`);
661
+ }
662
+ // Post to discussion if requested
663
+ if (args.discussionId && results.length > 0) {
664
+ const fileIds = results.map(r => r.fileId);
665
+ const message = batches.length > 1
666
+ ? `šŸ“„ Generated ${results.length} PDFs from ${totalActivities} activities (${batches.length} batches)`
667
+ : `šŸ“„ Generated document from ${totalActivities} ${totalActivities === 1 ? 'activity' : 'activities'}`;
668
+ await context.hailer.sendDiscussionMessage(args.discussionId, message, fileIds);
669
+ logger.debug('Posted PDFs to discussion', {
670
+ discussionId: args.discussionId,
671
+ fileCount: fileIds.length
672
+ });
673
+ }
674
+ // Build response
675
+ const totalSizeKB = results.reduce((sum, r) => sum + parseFloat(r.sizeKB || '0'), 0).toFixed(2);
676
+ if (batches.length === 1) {
677
+ // Single batch response
678
+ const r = results[0];
679
+ let responseText = `āœ… **Document generated successfully!**\n\n` +
680
+ `šŸ“„ **File Details:**\n` +
681
+ `- File ID: \`${r.fileId}\`\n` +
682
+ `- Size: ${r.sizeKB} KB\n` +
683
+ `- Activities: ${totalActivities}\n`;
684
+ if (args.discussionId) {
685
+ responseText += `\nšŸ’¬ **Posted to discussion**: ${args.discussionId}`;
686
+ }
687
+ else {
688
+ responseText += `\nšŸ’” **Next Steps:**\n` +
689
+ `- Download: \`download_file({ fileId: "${r.fileId}" })\`\n` +
690
+ `- Post to discussion: \`add_discussion_message({ discussionId: "...", fileIds: ["${r.fileId}"] })\``;
691
+ }
692
+ return { content: [{ type: "text", text: responseText }] };
693
+ }
694
+ // Multi-batch response
695
+ let responseText = `āœ… **Documents generated successfully!**\n\n` +
696
+ `šŸ“Š **Summary:**\n` +
697
+ `- Total activities: ${totalActivities}\n` +
698
+ `- Batches: ${batches.length} (${batchSize} per batch)\n` +
699
+ `- PDFs generated: ${results.length}\n` +
700
+ `- Total size: ${totalSizeKB} KB\n`;
701
+ if (errors.length > 0) {
702
+ responseText += `- Failed batches: ${errors.length}\n`;
703
+ }
704
+ responseText += `\nšŸ“„ **Generated Files:**\n`;
705
+ results.forEach(r => {
706
+ responseText += `- Batch ${r.batchNum}: \`${r.fileId}\` (${r.activityCount} activities, ${r.sizeKB} KB)\n`;
707
+ });
708
+ if (errors.length > 0) {
709
+ responseText += `\nāš ļø **Failed Batches:**\n`;
710
+ errors.forEach(e => {
711
+ responseText += `- Batch ${e.batchNum}: ${e.error}\n`;
712
+ });
713
+ }
714
+ if (args.discussionId) {
715
+ responseText += `\nšŸ’¬ **Posted to discussion**: ${args.discussionId}`;
716
+ }
717
+ return { content: [{ type: "text", text: responseText }] };
718
+ }
719
+ catch (error) {
720
+ if (!request_logger_1.RequestLogger.getCurrent())
721
+ logger.error("Failed to generate document", error);
722
+ const errorMessage = error instanceof Error
723
+ ? error.message
724
+ : (typeof error === 'object' && error !== null)
725
+ ? JSON.stringify(error, null, 2)
726
+ : String(error);
727
+ return {
728
+ content: [{
729
+ type: "text",
730
+ text: `āŒ Document generation failed: ${errorMessage}\n\n` +
731
+ `šŸ’” **Troubleshooting:**\n` +
732
+ `- Verify the template ID is correct (use list_document_templates)\n` +
733
+ `- Ensure all activity IDs are valid and accessible\n` +
734
+ `- For large batches, try reducing batchSize\n` +
735
+ `- Check that the template is compatible with the provided activities`
736
+ }]
737
+ };
738
+ }
739
+ }
740
+ };
741
+ //# sourceMappingURL=document.js.map