@hailer/mcp 1.1.12 → 1.1.14

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 (269) hide show
  1. package/CHANGELOG.md +0 -7
  2. package/{.claude → dist}/CLAUDE.md +2 -2
  3. package/dist/app.js +18 -5
  4. package/dist/bot/bot-config.d.ts +10 -1
  5. package/dist/bot/bot-config.js +64 -3
  6. package/dist/bot/bot-manager.d.ts +2 -0
  7. package/dist/bot/bot-manager.js +9 -2
  8. package/dist/bot/bot.d.ts +33 -0
  9. package/dist/bot/bot.js +461 -160
  10. package/dist/bot/services/message-classifier.js +17 -0
  11. package/dist/bot/services/permission-guard.d.ts +52 -0
  12. package/dist/bot/services/permission-guard.js +149 -0
  13. package/dist/bot/services/types.d.ts +5 -0
  14. package/dist/bot/services/typing-indicator.d.ts +6 -1
  15. package/dist/bot/services/typing-indicator.js +19 -3
  16. package/dist/cli.js +0 -0
  17. package/dist/config.d.ts +6 -1
  18. package/dist/config.js +43 -0
  19. package/dist/core.js +3 -6
  20. package/dist/lib/discussion-lock.d.ts +42 -0
  21. package/dist/lib/discussion-lock.js +110 -0
  22. package/dist/mcp/UserContextCache.d.ts +5 -0
  23. package/dist/mcp/UserContextCache.js +51 -19
  24. package/dist/mcp/hailer-clients.d.ts +19 -1
  25. package/dist/mcp/hailer-clients.js +158 -24
  26. package/dist/mcp/session-store.d.ts +68 -0
  27. package/dist/mcp/session-store.js +169 -0
  28. package/dist/mcp/signal-handler.js +2 -0
  29. package/dist/mcp/tool-registry.d.ts +17 -4
  30. package/dist/mcp/tool-registry.js +37 -7
  31. package/dist/mcp/tools/activity.js +99 -7
  32. package/dist/mcp/tools/app-scaffold.js +304 -336
  33. package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
  34. package/dist/mcp/tools/bot-config/constants.js +94 -0
  35. package/dist/mcp/tools/bot-config/core.d.ts +253 -0
  36. package/dist/mcp/tools/bot-config/core.js +2456 -0
  37. package/dist/mcp/tools/bot-config/index.d.ts +10 -0
  38. package/dist/mcp/tools/bot-config/index.js +59 -0
  39. package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
  40. package/dist/mcp/tools/bot-config/tools.js +15 -0
  41. package/dist/mcp/tools/bot-config/types.d.ts +50 -0
  42. package/dist/mcp/tools/bot-config/types.js +6 -0
  43. package/dist/mcp/tools/bug-fixer-tools.d.ts +45 -0
  44. package/dist/mcp/tools/bug-fixer-tools.js +1096 -0
  45. package/dist/mcp/tools/company.d.ts +9 -0
  46. package/dist/mcp/tools/company.js +88 -0
  47. package/dist/mcp/tools/discussion.js +68 -0
  48. package/dist/mcp/tools/document.d.ts +11 -0
  49. package/dist/mcp/tools/document.js +741 -0
  50. package/dist/mcp/tools/investigate.d.ts +9 -0
  51. package/dist/mcp/tools/investigate.js +254 -0
  52. package/dist/mcp/tools/workflow-permissions.d.ts +15 -0
  53. package/dist/mcp/tools/workflow-permissions.js +204 -0
  54. package/dist/mcp/tools/workflow.js +57 -18
  55. package/dist/mcp/utils/index.d.ts +2 -0
  56. package/dist/mcp/utils/index.js +12 -1
  57. package/dist/mcp/utils/role-utils.d.ts +74 -0
  58. package/dist/mcp/utils/role-utils.js +151 -0
  59. package/dist/mcp/utils/types.d.ts +43 -1
  60. package/dist/mcp/utils/types.js +14 -0
  61. package/dist/mcp/webhook-handler.d.ts +4 -0
  62. package/dist/mcp/webhook-handler.js +8 -0
  63. package/dist/mcp-server.d.ts +23 -2
  64. package/dist/mcp-server.js +639 -127
  65. package/dist/plugins/vipunen/client.d.ts +150 -0
  66. package/dist/plugins/vipunen/client.js +535 -0
  67. package/dist/plugins/vipunen/config/schema-config.json +19 -0
  68. package/dist/plugins/vipunen/config/schema-doc.json +22 -0
  69. package/dist/plugins/vipunen/index.d.ts +41 -0
  70. package/dist/plugins/vipunen/index.js +88 -0
  71. package/dist/plugins/vipunen/tools.d.ts +26 -0
  72. package/dist/plugins/vipunen/tools.js +501 -0
  73. package/dist/stdio-server.d.ts +14 -0
  74. package/dist/stdio-server.js +101 -0
  75. package/package.json +2 -1
  76. package/.claude/agents/agent-ada-skill-builder.md +0 -94
  77. package/.claude/agents/agent-alejandro-function-fields.md +0 -342
  78. package/.claude/agents/agent-bjorn-config-audit.md +0 -103
  79. package/.claude/agents/agent-builder-agent-creator.md +0 -130
  80. package/.claude/agents/agent-code-simplifier.md +0 -53
  81. package/.claude/agents/agent-dmitri-activity-crud.md +0 -159
  82. package/.claude/agents/agent-giuseppe-app-builder.md +0 -247
  83. package/.claude/agents/agent-gunther-mcp-tools.md +0 -39
  84. package/.claude/agents/agent-helga-workflow-config.md +0 -204
  85. package/.claude/agents/agent-igor-activity-mover-automation.md +0 -125
  86. package/.claude/agents/agent-ingrid-doc-templates.md +0 -261
  87. package/.claude/agents/agent-ivan-monolith.md +0 -154
  88. package/.claude/agents/agent-kenji-data-reader.md +0 -86
  89. package/.claude/agents/agent-lars-code-inspector.md +0 -102
  90. package/.claude/agents/agent-marco-mockup-builder.md +0 -110
  91. package/.claude/agents/agent-marcus-api-documenter.md +0 -323
  92. package/.claude/agents/agent-marketplace-publisher.md +0 -280
  93. package/.claude/agents/agent-marketplace-reviewer.md +0 -309
  94. package/.claude/agents/agent-permissions-handler.md +0 -208
  95. package/.claude/agents/agent-simple-writer.md +0 -48
  96. package/.claude/agents/agent-svetlana-code-review.md +0 -171
  97. package/.claude/agents/agent-tanya-test-runner.md +0 -333
  98. package/.claude/agents/agent-ui-designer.md +0 -100
  99. package/.claude/agents/agent-viktor-sql-insights.md +0 -212
  100. package/.claude/agents/agent-web-search.md +0 -55
  101. package/.claude/agents/agent-yevgeni-discussions.md +0 -45
  102. package/.claude/agents/agent-zara-zapier.md +0 -159
  103. package/.claude/commands/app-squad.md +0 -135
  104. package/.claude/commands/audit-squad.md +0 -158
  105. package/.claude/commands/autoplan.md +0 -563
  106. package/.claude/commands/cleanup-squad.md +0 -98
  107. package/.claude/commands/config-squad.md +0 -106
  108. package/.claude/commands/crud-squad.md +0 -87
  109. package/.claude/commands/data-squad.md +0 -97
  110. package/.claude/commands/debug-squad.md +0 -303
  111. package/.claude/commands/doc-squad.md +0 -65
  112. package/.claude/commands/handoff.md +0 -137
  113. package/.claude/commands/health.md +0 -49
  114. package/.claude/commands/help.md +0 -29
  115. package/.claude/commands/help:agents.md +0 -151
  116. package/.claude/commands/help:commands.md +0 -78
  117. package/.claude/commands/help:faq.md +0 -79
  118. package/.claude/commands/help:plugins.md +0 -50
  119. package/.claude/commands/help:skills.md +0 -93
  120. package/.claude/commands/help:tools.md +0 -75
  121. package/.claude/commands/hotfix-squad.md +0 -112
  122. package/.claude/commands/integration-squad.md +0 -82
  123. package/.claude/commands/janitor-squad.md +0 -167
  124. package/.claude/commands/learn-auto.md +0 -120
  125. package/.claude/commands/learn.md +0 -120
  126. package/.claude/commands/mcp-list.md +0 -27
  127. package/.claude/commands/onboard-squad.md +0 -140
  128. package/.claude/commands/plan-workspace.md +0 -732
  129. package/.claude/commands/prd.md +0 -130
  130. package/.claude/commands/project-status.md +0 -82
  131. package/.claude/commands/publish.md +0 -138
  132. package/.claude/commands/recap.md +0 -69
  133. package/.claude/commands/restore.md +0 -64
  134. package/.claude/commands/review-squad.md +0 -152
  135. package/.claude/commands/save.md +0 -24
  136. package/.claude/commands/stats.md +0 -19
  137. package/.claude/commands/swarm.md +0 -210
  138. package/.claude/commands/tool-builder.md +0 -39
  139. package/.claude/commands/ws-pull.md +0 -44
  140. package/.claude/hooks/_shared-memory.cjs +0 -305
  141. package/.claude/hooks/_utils.cjs +0 -108
  142. package/.claude/hooks/agent-failure-detector.cjs +0 -383
  143. package/.claude/hooks/agent-usage-logger.cjs +0 -204
  144. package/.claude/hooks/app-edit-guard.cjs +0 -494
  145. package/.claude/hooks/auto-learn.cjs +0 -304
  146. package/.claude/hooks/bash-guard.cjs +0 -272
  147. package/.claude/hooks/builder-mode-manager.cjs +0 -354
  148. package/.claude/hooks/bulk-activity-guard.cjs +0 -271
  149. package/.claude/hooks/context-watchdog.cjs +0 -230
  150. package/.claude/hooks/delegation-reminder.cjs +0 -465
  151. package/.claude/hooks/design-system-lint.cjs +0 -271
  152. package/.claude/hooks/post-scaffold-hook.cjs +0 -181
  153. package/.claude/hooks/prompt-guard.cjs +0 -354
  154. package/.claude/hooks/publish-template-guard.cjs +0 -147
  155. package/.claude/hooks/session-start.cjs +0 -35
  156. package/.claude/hooks/shared-memory-writer.cjs +0 -147
  157. package/.claude/hooks/skill-injector.cjs +0 -140
  158. package/.claude/hooks/skill-usage-logger.cjs +0 -258
  159. package/.claude/hooks/src-edit-guard.cjs +0 -240
  160. package/.claude/hooks/sync-marketplace-agents.cjs +0 -346
  161. package/.claude/settings.json +0 -257
  162. package/.claude/skills/SDK-activity-patterns/SKILL.md +0 -428
  163. package/.claude/skills/SDK-document-templates/SKILL.md +0 -1033
  164. package/.claude/skills/SDK-function-fields/SKILL.md +0 -542
  165. package/.claude/skills/SDK-generate-skill/SKILL.md +0 -92
  166. package/.claude/skills/SDK-init-skill/SKILL.md +0 -127
  167. package/.claude/skills/SDK-insight-queries/SKILL.md +0 -787
  168. package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -1139
  169. package/.claude/skills/agent-structure/SKILL.md +0 -98
  170. package/.claude/skills/api-documentation-patterns/SKILL.md +0 -474
  171. package/.claude/skills/chrome-mcp-reference/SKILL.md +0 -370
  172. package/.claude/skills/delegation-routing/SKILL.md +0 -202
  173. package/.claude/skills/frontend-design/SKILL.md +0 -254
  174. package/.claude/skills/hailer-activity-mover/SKILL.md +0 -213
  175. package/.claude/skills/hailer-api-client/SKILL.md +0 -518
  176. package/.claude/skills/hailer-app-builder/SKILL.md +0 -1434
  177. package/.claude/skills/hailer-apps-pictures/SKILL.md +0 -269
  178. package/.claude/skills/hailer-design-system/SKILL.md +0 -235
  179. package/.claude/skills/hailer-monolith-automations/SKILL.md +0 -686
  180. package/.claude/skills/hailer-permissions-system/SKILL.md +0 -121
  181. package/.claude/skills/hailer-project-protocol/SKILL.md +0 -488
  182. package/.claude/skills/hailer-rest-api/SKILL.md +0 -61
  183. package/.claude/skills/hailer-rest-api/hailer-activities.md +0 -184
  184. package/.claude/skills/hailer-rest-api/hailer-admin.md +0 -473
  185. package/.claude/skills/hailer-rest-api/hailer-calendar.md +0 -256
  186. package/.claude/skills/hailer-rest-api/hailer-feed.md +0 -249
  187. package/.claude/skills/hailer-rest-api/hailer-insights.md +0 -195
  188. package/.claude/skills/hailer-rest-api/hailer-messaging.md +0 -276
  189. package/.claude/skills/hailer-rest-api/hailer-workflows.md +0 -283
  190. package/.claude/skills/insight-join-patterns/SKILL.md +0 -174
  191. package/.claude/skills/integration-patterns/SKILL.md +0 -421
  192. package/.claude/skills/json-only-output/SKILL.md +0 -72
  193. package/.claude/skills/lsp-setup/SKILL.md +0 -160
  194. package/.claude/skills/mcp-direct-tools/SKILL.md +0 -153
  195. package/.claude/skills/optional-parameters/SKILL.md +0 -72
  196. package/.claude/skills/publish-hailer-app/SKILL.md +0 -244
  197. package/.claude/skills/testing-patterns/SKILL.md +0 -630
  198. package/.claude/skills/tool-builder/SKILL.md +0 -250
  199. package/.claude/skills/tool-parameter-usage/SKILL.md +0 -126
  200. package/.claude/skills/tool-response-verification/SKILL.md +0 -92
  201. package/.claude/skills/zapier-hailer-patterns/SKILL.md +0 -581
  202. package/.mcp.json +0 -13
  203. package/.opencode/agent/agent-ada-skill-builder.md +0 -35
  204. package/.opencode/agent/agent-alejandro-function-fields.md +0 -39
  205. package/.opencode/agent/agent-bjorn-config-audit.md +0 -36
  206. package/.opencode/agent/agent-builder-agent-creator.md +0 -39
  207. package/.opencode/agent/agent-code-simplifier.md +0 -31
  208. package/.opencode/agent/agent-dmitri-activity-crud.md +0 -40
  209. package/.opencode/agent/agent-giuseppe-app-builder.md +0 -37
  210. package/.opencode/agent/agent-gunther-mcp-tools.md +0 -39
  211. package/.opencode/agent/agent-helga-workflow-config.md +0 -203
  212. package/.opencode/agent/agent-igor-activity-mover-automation.md +0 -46
  213. package/.opencode/agent/agent-ingrid-doc-templates.md +0 -39
  214. package/.opencode/agent/agent-ivan-monolith.md +0 -46
  215. package/.opencode/agent/agent-kenji-data-reader.md +0 -53
  216. package/.opencode/agent/agent-lars-code-inspector.md +0 -28
  217. package/.opencode/agent/agent-marco-mockup-builder.md +0 -42
  218. package/.opencode/agent/agent-marcus-api-documenter.md +0 -53
  219. package/.opencode/agent/agent-marketplace-publisher.md +0 -44
  220. package/.opencode/agent/agent-marketplace-reviewer.md +0 -42
  221. package/.opencode/agent/agent-permissions-handler.md +0 -50
  222. package/.opencode/agent/agent-simple-writer.md +0 -45
  223. package/.opencode/agent/agent-svetlana-code-review.md +0 -39
  224. package/.opencode/agent/agent-tanya-test-runner.md +0 -57
  225. package/.opencode/agent/agent-ui-designer.md +0 -56
  226. package/.opencode/agent/agent-viktor-sql-insights.md +0 -34
  227. package/.opencode/agent/agent-web-search.md +0 -42
  228. package/.opencode/agent/agent-yevgeni-discussions.md +0 -37
  229. package/.opencode/agent/agent-zara-zapier.md +0 -53
  230. package/.opencode/commands/app-squad.md +0 -135
  231. package/.opencode/commands/audit-squad.md +0 -158
  232. package/.opencode/commands/autoplan.md +0 -563
  233. package/.opencode/commands/cleanup-squad.md +0 -98
  234. package/.opencode/commands/config-squad.md +0 -106
  235. package/.opencode/commands/crud-squad.md +0 -87
  236. package/.opencode/commands/data-squad.md +0 -97
  237. package/.opencode/commands/debug-squad.md +0 -303
  238. package/.opencode/commands/doc-squad.md +0 -65
  239. package/.opencode/commands/handoff.md +0 -137
  240. package/.opencode/commands/health.md +0 -49
  241. package/.opencode/commands/help-agents.md +0 -151
  242. package/.opencode/commands/help-commands.md +0 -32
  243. package/.opencode/commands/help-faq.md +0 -29
  244. package/.opencode/commands/help-plugins.md +0 -28
  245. package/.opencode/commands/help-skills.md +0 -7
  246. package/.opencode/commands/help-tools.md +0 -40
  247. package/.opencode/commands/help.md +0 -28
  248. package/.opencode/commands/hotfix-squad.md +0 -112
  249. package/.opencode/commands/integration-squad.md +0 -82
  250. package/.opencode/commands/janitor-squad.md +0 -167
  251. package/.opencode/commands/learn-auto.md +0 -120
  252. package/.opencode/commands/learn.md +0 -120
  253. package/.opencode/commands/mcp-list.md +0 -27
  254. package/.opencode/commands/onboard-squad.md +0 -140
  255. package/.opencode/commands/plan-workspace.md +0 -732
  256. package/.opencode/commands/prd.md +0 -131
  257. package/.opencode/commands/project-status.md +0 -82
  258. package/.opencode/commands/publish.md +0 -138
  259. package/.opencode/commands/recap.md +0 -69
  260. package/.opencode/commands/restore.md +0 -64
  261. package/.opencode/commands/review-squad.md +0 -152
  262. package/.opencode/commands/save.md +0 -24
  263. package/.opencode/commands/stats.md +0 -19
  264. package/.opencode/commands/swarm.md +0 -210
  265. package/.opencode/commands/tool-builder.md +0 -39
  266. package/.opencode/commands/ws-pull.md +0 -44
  267. package/.opencode/opencode.json +0 -28
  268. package/SESSION-HANDOFF.md +0 -68
  269. package/inbox/2026-03-04-bot-config-patterns.md +0 -24
@@ -1,174 +0,0 @@
1
- ---
2
- name: insight-join-patterns
3
- description: Correct JOIN syntax for Hailer insights with ActivityLink fields
4
- version: 1.0.0
5
- triggers: JOIN query errors, missing columns, NULL results in insight queries
6
- ---
7
-
8
- **Prerequisite:** Before using JOINs, review `SDK-insight-queries` skill for basic insight syntax and single-workflow queries.
9
-
10
- <problem>
11
- When joining workflows with ActivityLink fields in Hailer insights, you must:
12
- 1. Include `_id` meta field in BOTH source definitions
13
- 2. Join ON the activitylink field value equals target _id
14
- 3. Use the activitylink fieldId (NOT the key) for the JOIN condition
15
- </problem>
16
-
17
- <rules>
18
- - Both workflows need `{ name: 'id', meta: '_id' }` in their fields array
19
- - JOIN condition: `source1.activityLinkFieldName = source2.id`
20
- - Use LEFT JOIN for optional relationships (activitylink can be null)
21
- - Use INNER JOIN only when relationship must exist
22
- </rules>
23
-
24
- <correct>
25
- ```javascript
26
- // Players workflow has "club" field (activitylink to Clubs workflow)
27
- {
28
- sources: [
29
- {
30
- name: 'p',
31
- workflowId: '68446dc05b30685f67c6fcd4',
32
- fields: [
33
- { name: 'player_name', meta: 'name' },
34
- { name: 'id', meta: '_id' }, // Required!
35
- { name: 'club', fieldId: '684d5e45...' } // ActivityLink field
36
- ]
37
- },
38
- {
39
- name: 'c',
40
- workflowId: '691ea936ccb6bdeebc0cbf77',
41
- fields: [
42
- { name: 'club_name', meta: 'name' },
43
- { name: 'id', meta: '_id' } // Required!
44
- ]
45
- }
46
- ],
47
- query: 'SELECT p.player_name, c.club_name FROM p LEFT JOIN c ON p.club = c.id'
48
- }
49
- ```
50
- </correct>
51
-
52
- <wrong>
53
- ```javascript
54
- // ❌ WRONG - Missing _id in clubs source
55
- {
56
- sources: [
57
- {
58
- name: 'p',
59
- workflowId: 'players-id',
60
- fields: [
61
- { name: 'player_name', meta: 'name' },
62
- { name: 'id', meta: '_id' },
63
- { name: 'club', fieldId: 'club-field-id' }
64
- ]
65
- },
66
- {
67
- name: 'c',
68
- workflowId: 'clubs-id',
69
- fields: [
70
- { name: 'club_name', meta: 'name' }
71
- // Missing: { name: 'id', meta: '_id' }
72
- ]
73
- }
74
- ],
75
- query: 'SELECT p.player_name, c.club_name FROM p LEFT JOIN c ON p.club = c.id'
76
- }
77
- // Error: "no such column: c.id"
78
- ```
79
- </wrong>
80
-
81
- <examples>
82
- ### Three-Way JOIN (Tasks -> Topics -> Projects)
83
- ```javascript
84
- {
85
- sources: [
86
- {
87
- name: 't',
88
- workflowId: 'tasks-workflow-id',
89
- fields: [
90
- { name: 'task_name', meta: 'name' },
91
- { name: 'id', meta: '_id' },
92
- { name: 'topic', fieldId: 'topic-field-id' }
93
- ]
94
- },
95
- {
96
- name: 'top',
97
- workflowId: 'topics-workflow-id',
98
- fields: [
99
- { name: 'topic_name', meta: 'name' },
100
- { name: 'id', meta: '_id' },
101
- { name: 'project', fieldId: 'project-field-id' }
102
- ]
103
- },
104
- {
105
- name: 'p',
106
- workflowId: 'projects-workflow-id',
107
- fields: [
108
- { name: 'project_name', meta: 'name' },
109
- { name: 'id', meta: '_id' }
110
- ]
111
- }
112
- ],
113
- query: `
114
- SELECT t.task_name, top.topic_name, p.project_name
115
- FROM t
116
- LEFT JOIN top ON t.topic = top.id
117
- LEFT JOIN p ON top.project = p.id
118
- `
119
- }
120
- ```
121
-
122
- ### Aggregation with JOIN
123
- ```javascript
124
- {
125
- sources: [
126
- {
127
- name: 'matches',
128
- workflowId: 'matches-workflow-id',
129
- fields: [
130
- { name: 'match_date', fieldId: 'date-field-id' },
131
- { name: 'id', meta: '_id' },
132
- { name: 'home_team', fieldId: 'home-team-field-id' }
133
- ]
134
- },
135
- {
136
- name: 'teams',
137
- workflowId: 'teams-workflow-id',
138
- fields: [
139
- { name: 'team_name', meta: 'name' },
140
- { name: 'id', meta: '_id' }
141
- ]
142
- }
143
- ],
144
- query: `
145
- SELECT teams.team_name, COUNT(*) as match_count
146
- FROM matches
147
- LEFT JOIN teams ON matches.home_team = teams.id
148
- GROUP BY teams.team_name
149
- `
150
- }
151
- ```
152
- </examples>
153
-
154
- <troubleshooting>
155
- **Error: "no such column: c.id"**
156
- - Missing `{ name: 'id', meta: '_id' }` in target workflow source
157
-
158
- **Error: "no such column: p.club"**
159
- - ActivityLink field not included in source fields array
160
- - Check you used correct fieldId from `get_workflow_schema`
161
-
162
- **NULL results for joined data**
163
- - ActivityLink field is empty/null for some activities (expected with LEFT JOIN)
164
- - Use INNER JOIN if you only want activities with relationships
165
- </troubleshooting>
166
-
167
- <checklist>
168
- Before creating an insight with JOINs:
169
- - [ ] Both workflow sources include `{ name: 'id', meta: '_id' }`
170
- - [ ] ActivityLink field uses `fieldId` (NOT `key`)
171
- - [ ] JOIN condition uses column names from sources (e.g., `p.club = c.id`)
172
- - [ ] Using LEFT JOIN (unless relationship required)
173
- - [ ] Tested with `preview_insight` first
174
- </checklist>
@@ -1,421 +0,0 @@
1
- ---
2
- name: integration-patterns
3
- description: Hailer integration microservice patterns - activity movers, webhooks, SCIM, Kafka consumers
4
- version: 1.0.0
5
- triggers: Build integration, activity mover, webhook handler, SCIM endpoint, Kafka consumer
6
- ---
7
-
8
- # Integration Patterns
9
-
10
- ## Architecture Overview
11
-
12
- ```
13
- ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
14
- │ External │ │ Integration │ │ Hailer API │
15
- │ System │────▶│ Service │────▶│ │
16
- │ (Kafka/HTTP) │ │ (Node.js) │ │ (WebSocket) │
17
- └─────────────────┘ └─────────────────┘ └─────────────────┘
18
-
19
-
20
- ┌─────────────────┐
21
- │ Logging & │
22
- │ Monitoring │
23
- └─────────────────┘
24
- ```
25
-
26
- ---
27
-
28
- ## 1. Activity Mover Pattern
29
-
30
- Monitors activities and moves linked activities when triggers fire.
31
-
32
- ### Configuration Structure
33
-
34
- ```typescript
35
- interface ActivityMoverConfig {
36
- logDiscussionId: string; // Discussion for audit logging
37
- email: string; // Integration user email
38
- password: string; // Or "ENV:HAILER_PASSWORD"
39
- project: string; // Project identifier
40
- triggers: Trigger[];
41
- }
42
-
43
- interface Trigger {
44
- processId: string; // Source workflow ID
45
- phaseId: string; // Phase that triggers action
46
- metaDataId: string; // Hidden field for tracking ("Seen"/"Not seen")
47
- linkedProcesses: LinkedProcess[];
48
- }
49
-
50
- interface LinkedProcess {
51
- processId: string; // Target workflow ID
52
- sourcePhases: string[]; // Move FROM these phases
53
- targetPhase: string; // Move TO this phase
54
- }
55
- ```
56
-
57
- ### Example Config
58
-
59
- ```json
60
- {
61
- "logDiscussionId": "694c9536acfa30f6df13201b",
62
- "email": "integration@workspace.com",
63
- "password": "ENV:HAILER_PASSWORD",
64
- "project": "orders-sync",
65
- "triggers": [
66
- {
67
- "processId": "67dc1b7d3d2c9f6cf9a5468d",
68
- "phaseId": "67dc1b7d3d2c9f6cf9a546c4",
69
- "metaDataId": "67e697da6ada809b961c35b5",
70
- "linkedProcesses": [
71
- {
72
- "processId": "67dc1b7d3d2c9f6cf9a54690",
73
- "sourcePhases": ["67dc1b7d3d2c9f6cf9a54691", "67dc1b7d3d2c9f6cf9a54692"],
74
- "targetPhase": "67dc1b7d3d2c9f6cf9a54695"
75
- }
76
- ]
77
- }
78
- ]
79
- }
80
- ```
81
-
82
- ### Hailer Setup Requirements
83
-
84
- 1. **Metadata field** on trigger workflow:
85
- - Type: TEXT (hidden)
86
- - Default value: "Not seen"
87
- - Used to track processed activities
88
-
89
- 2. **Link field** connecting workflows:
90
- - Activity link from trigger workflow to target workflow
91
-
92
- 3. **Integration user**:
93
- - Dedicated user account
94
- - Edit permissions on both workflows
95
-
96
- 4. **Discussion activity**:
97
- - For audit logging
98
- - Integration user must have access
99
-
100
- ### Core Logic
101
-
102
- ```typescript
103
- // Listen for activity updates
104
- client.on('activities.updated', async (data) => {
105
- for (const trigger of config.triggers) {
106
- if (data.processId === trigger.processId && data.phaseId === trigger.phaseId) {
107
- await processTrigger(data.activityId, trigger);
108
- }
109
- }
110
- });
111
-
112
- async function processTrigger(activityId: string, trigger: Trigger) {
113
- // Check if already processed
114
- const activity = await client.request('v3.activity.get', [activityId]);
115
- if (activity.fields[trigger.metaDataId]?.value === 'Seen') return;
116
-
117
- // Get linked activities
118
- const linked = await client.request('v3.activity.linkedFrom.overview', [activityId]);
119
-
120
- // Move matching activities
121
- for (const process of trigger.linkedProcesses) {
122
- const toMove = linked.filter(a =>
123
- a.processId === process.processId &&
124
- process.sourcePhases.includes(a.phaseId)
125
- );
126
-
127
- if (toMove.length > 0) {
128
- await client.request('v3.activity.updateMany', [
129
- toMove.map(a => a._id),
130
- { phaseId: process.targetPhase }
131
- ]);
132
- }
133
- }
134
-
135
- // Mark as processed
136
- await client.request('v3.activity.update', [activityId, {
137
- fields: { [trigger.metaDataId]: 'Seen' }
138
- }]);
139
- }
140
- ```
141
-
142
- ---
143
-
144
- ## 2. Webhook Handler Pattern
145
-
146
- HTTP endpoints that trigger Hailer operations.
147
-
148
- ### Express Setup
149
-
150
- ```typescript
151
- import express from 'express';
152
- import crypto from 'crypto';
153
-
154
- const app = express();
155
- app.use(express.json());
156
-
157
- // Webhook signature validation
158
- function validateSignature(req: express.Request, secret: string): boolean {
159
- const signature = req.headers['x-webhook-signature'];
160
- const payload = JSON.stringify(req.body);
161
- const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex');
162
- return signature === expected;
163
- }
164
-
165
- // Webhook endpoint
166
- app.post('/api/v1/webhook/:type', async (req, res) => {
167
- const { type } = req.params;
168
-
169
- if (!validateSignature(req, config.webhookSecret)) {
170
- return res.status(401).json({ error: 'Invalid signature' });
171
- }
172
-
173
- try {
174
- switch (type) {
175
- case 'order.created':
176
- await handleOrderCreated(req.body);
177
- break;
178
- case 'order.updated':
179
- await handleOrderUpdated(req.body);
180
- break;
181
- default:
182
- logger.warn({ type }, 'Unknown webhook type');
183
- }
184
- res.json({ status: 'ok' });
185
- } catch (error) {
186
- logger.error({ error, type }, 'Webhook processing failed');
187
- res.status(500).json({ error: 'Processing failed' });
188
- }
189
- });
190
- ```
191
-
192
- ### Webhook Handler Example
193
-
194
- ```typescript
195
- async function handleOrderCreated(payload: OrderPayload) {
196
- // Map external data to Hailer fields
197
- const fields = {
198
- [FieldIds.external_id]: payload.orderId,
199
- [FieldIds.customer_name]: payload.customer.name,
200
- [FieldIds.total_amount]: payload.total,
201
- [FieldIds.order_date]: new Date(payload.createdAt).getTime()
202
- };
203
-
204
- // Create activity in Hailer
205
- const result = await hailerClient.createActivity(
206
- WorkflowIds.orders,
207
- PhaseIds.new,
208
- fields
209
- );
210
-
211
- logger.info({ orderId: payload.orderId, activityId: result._id }, 'Order created in Hailer');
212
- }
213
- ```
214
-
215
- ---
216
-
217
- ## 3. SCIM 2.0 Pattern
218
-
219
- User provisioning from identity providers (Microsoft Entra, Okta).
220
-
221
- ### Endpoints
222
-
223
- | Method | Path | Description |
224
- |--------|------|-------------|
225
- | POST | /scim/v2/Users | Create user |
226
- | GET | /scim/v2/Users/:id | Get user |
227
- | PATCH | /scim/v2/Users/:id | Update user |
228
- | DELETE | /scim/v2/Users/:id | Deactivate user |
229
- | GET | /scim/v2/Users | List/filter users |
230
-
231
- ### User Mapping
232
-
233
- ```typescript
234
- interface ScimUser {
235
- schemas: string[];
236
- userName: string;
237
- name: { givenName: string; familyName: string };
238
- emails: { value: string; primary: boolean }[];
239
- active: boolean;
240
- roles?: { value: string }[];
241
- }
242
-
243
- function mapScimToHailer(scim: ScimUser): HailerUserPayload {
244
- return {
245
- email: scim.emails.find(e => e.primary)?.value || scim.userName,
246
- firstName: scim.name.givenName,
247
- lastName: scim.name.familyName,
248
- active: scim.active,
249
- teams: mapRolesToTeams(scim.roles)
250
- };
251
- }
252
-
253
- function mapRolesToTeams(roles?: { value: string }[]): string[] {
254
- const teamMapping: Record<string, string> = {
255
- 'admin': TeamIds.admins,
256
- 'manager': TeamIds.managers,
257
- 'user': TeamIds.users
258
- };
259
- return (roles || [])
260
- .map(r => teamMapping[r.value])
261
- .filter(Boolean);
262
- }
263
- ```
264
-
265
- ### SCIM Response Format
266
-
267
- ```typescript
268
- function toScimUser(hailerUser: HailerUser, baseUrl: string): ScimUser {
269
- return {
270
- schemas: ['urn:ietf:params:scim:schemas:core:2.0:User'],
271
- id: hailerUser._id,
272
- userName: hailerUser.email,
273
- name: {
274
- givenName: hailerUser.firstName,
275
- familyName: hailerUser.lastName
276
- },
277
- emails: [{ value: hailerUser.email, primary: true }],
278
- active: hailerUser.active,
279
- meta: {
280
- resourceType: 'User',
281
- location: `${baseUrl}/scim/v2/Users/${hailerUser._id}`
282
- }
283
- };
284
- }
285
- ```
286
-
287
- ---
288
-
289
- ## 4. Kafka Consumer Pattern
290
-
291
- Event-driven processing from message queues.
292
-
293
- ### Consumer Setup
294
-
295
- ```typescript
296
- import { Kafka, Consumer, EachMessagePayload } from 'kafkajs';
297
-
298
- const kafka = new Kafka({
299
- clientId: 'hailer-integration',
300
- brokers: config.kafkaBrokers,
301
- ssl: {
302
- ca: [fs.readFileSync(config.caCertPath)],
303
- key: fs.readFileSync(config.keyPath),
304
- cert: fs.readFileSync(config.certPath)
305
- }
306
- });
307
-
308
- const consumer = kafka.consumer({ groupId: 'hailer-consumer-group' });
309
-
310
- async function startConsumer() {
311
- await consumer.connect();
312
- await consumer.subscribe({ topic: 'events', fromBeginning: false });
313
-
314
- await consumer.run({
315
- eachMessage: async ({ topic, partition, message }: EachMessagePayload) => {
316
- const event = JSON.parse(message.value.toString());
317
-
318
- try {
319
- await processEvent(event);
320
- logger.info({ eventId: event.id, offset: message.offset }, 'Event processed');
321
- } catch (error) {
322
- logger.error({ error, event }, 'Event processing failed');
323
- await sendToDeadLetterQueue(message);
324
- }
325
- }
326
- });
327
- }
328
- ```
329
-
330
- ### Event Processing
331
-
332
- ```typescript
333
- async function processEvent(event: ExternalEvent) {
334
- switch (event.type) {
335
- case 'student.created':
336
- await createStudent(event.data);
337
- break;
338
- case 'student.updated':
339
- await updateStudent(event.data);
340
- break;
341
- case 'student.deleted':
342
- await deactivateStudent(event.data);
343
- break;
344
- default:
345
- logger.warn({ eventType: event.type }, 'Unknown event type');
346
- }
347
- }
348
- ```
349
-
350
- ---
351
-
352
- ## Common Patterns
353
-
354
- ### Retry with Exponential Backoff
355
-
356
- ```typescript
357
- async function withRetry<T>(
358
- fn: () => Promise<T>,
359
- maxRetries: number = 3,
360
- baseDelay: number = 1000
361
- ): Promise<T> {
362
- let lastError: Error;
363
-
364
- for (let i = 0; i < maxRetries; i++) {
365
- try {
366
- return await fn();
367
- } catch (error) {
368
- lastError = error;
369
- const delay = baseDelay * Math.pow(2, i);
370
- logger.warn({ attempt: i + 1, delay, error: error.message }, 'Retrying...');
371
- await sleep(delay);
372
- }
373
- }
374
-
375
- throw lastError;
376
- }
377
- ```
378
-
379
- ### Graceful Shutdown
380
-
381
- ```typescript
382
- async function gracefulShutdown(signal: string) {
383
- logger.info({ signal }, 'Shutdown signal received');
384
-
385
- // Stop accepting new requests
386
- server.close();
387
-
388
- // Disconnect from Hailer
389
- await hailerClient.disconnect();
390
-
391
- // Disconnect from Kafka
392
- await consumer.disconnect();
393
-
394
- logger.info('Graceful shutdown complete');
395
- process.exit(0);
396
- }
397
-
398
- process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
399
- process.on('SIGINT', () => gracefulShutdown('SIGINT'));
400
- ```
401
-
402
- ### Health Check
403
-
404
- ```typescript
405
- app.get('/api/v1/health', async (req, res) => {
406
- const checks = {
407
- hailer: await checkHailerConnection(),
408
- kafka: await checkKafkaConnection(),
409
- uptime: process.uptime(),
410
- memory: process.memoryUsage(),
411
- lastActivity: lastActivityTimestamp
412
- };
413
-
414
- const healthy = checks.hailer.connected && checks.kafka.connected;
415
- res.status(healthy ? 200 : 503).json({
416
- status: healthy ? 'healthy' : 'unhealthy',
417
- timestamp: new Date().toISOString(),
418
- checks
419
- });
420
- });
421
- ```
@@ -1,72 +0,0 @@
1
- ---
2
- name: json-only-output
3
- description: Fix agents adding prose after JSON responses
4
- version: 1.0.1
5
- triggers: Agent outputs explanation text after valid JSON
6
- ---
7
-
8
- # JSON Only Output
9
-
10
- <purpose>
11
- Ensure agents output ONLY valid JSON with no prose, explanations, or commentary after the closing brace.
12
- </purpose>
13
-
14
- <why-this-happens>
15
- ## Why Agents Add Prose
16
-
17
- LLMs have a natural tendency to be helpful and explanatory. After completing a task, they want to:
18
- - Explain what they did
19
- - Suggest next steps
20
- - Provide context
21
- - Confirm their understanding
22
-
23
- This is normally good behavior, but for **subagents** returning structured data to an orchestrator, it breaks JSON parsing.
24
-
25
- **The `<protocol>` section isn't enough** because:
26
- - LLMs treat protocol as "guidelines" not hard rules
27
- - The helpful instinct overrides weak instructions
28
- - Protocol is at the end of the prompt, less salient
29
-
30
- **Why identity/rules work:**
31
- - `<identity>` shapes the agent's core persona ("I output JSON. Full stop.")
32
- - `<rules>` are processed as constraints, not suggestions
33
- - Positioned early in prompt, higher salience
34
- - Explicit prohibition is stronger than implicit expectation
35
- </why-this-happens>
36
-
37
- <patterns>
38
- ## Pattern 1: Stop at the Closing Brace
39
-
40
- Output JSON. Full stop. Nothing after the closing brace.
41
-
42
- ## Pattern 2: Agent Configuration Fix
43
-
44
- 1. Add to `<identity>`: "Output JSON. Full stop."
45
- 2. Add to `<rules>`: **JSON ONLY** - Output closing brace, then STOP. Zero prose after JSON.
46
- 3. Protocol section is NOT enough - agents ignore it without identity/rules reinforcement.
47
-
48
- ## Pattern 3: Include Summary Inside JSON
49
-
50
- If context is helpful, include it IN the JSON `summary` field, not after the JSON.
51
- </patterns>
52
-
53
- <examples>
54
- ## Example 1: Correct - Pure JSON Output
55
-
56
- ```json
57
- {"status":"success","result":{"fields":["taskName","priority"]},"summary":"Read 2 fields"}
58
- ```
59
- **STOP HERE. Nothing after closing brace.**
60
-
61
- ## Example 2: Wrong - Prose After JSON
62
-
63
- ```json
64
- {"status":"success","result":{"fields":["taskName","priority"]},"summary":"Read 2 fields"}
65
- ```
66
-
67
- The workflow has 2 fields defined in workspace/Tasks_123/fields.ts. You can now use these field IDs with dmitri for activity creation.
68
-
69
- **Action Required**: Run `npm run pull` to refresh.
70
-
71
- This violates JSON-only protocol by adding explanation text AFTER the valid JSON response.
72
- </examples>