@hailer/mcp 1.0.29 → 1.1.3

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 -253
  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 +43 -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 +227 -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,78 @@
1
+ /**
2
+ * Token Billing Service
3
+ *
4
+ * Handles real-time token burning per workspace with balance checks at session start.
5
+ *
6
+ * Workspace Isolation:
7
+ * - Each workspace has its own token balance (managed by Hailer backend)
8
+ * - Balance check and burn calls include workspaceId for correct routing
9
+ *
10
+ * Behavior:
11
+ * - Check balance when new session starts -> reject if negative for that workspace
12
+ * - Burn tokens after each LLM API call -> real-time, charges correct workspace
13
+ * - Allow balance to go negative during active session -> don't interrupt
14
+ */
15
+ import { Logger } from "../../lib/logger";
16
+ import { HailerApiClient } from "../../mcp/utils/hailer-api-client";
17
+ export interface TokenBurnPayload {
18
+ workspaceId: string;
19
+ inputTokens: number;
20
+ outputTokens: number;
21
+ cacheCreationTokens: number;
22
+ cacheReadTokens: number;
23
+ costUsd: number;
24
+ sessionId?: string;
25
+ activityId?: string;
26
+ model?: string;
27
+ }
28
+ export interface BalanceResponse {
29
+ workspaceId: string;
30
+ balance: number;
31
+ isNegative: boolean;
32
+ hasBalance: boolean;
33
+ }
34
+ export interface TokenBillingConfig {
35
+ enabled: boolean;
36
+ }
37
+ export declare class TokenBillingService {
38
+ private logger;
39
+ private hailerClient;
40
+ private balanceCache;
41
+ private pendingBalanceChecks;
42
+ private static CACHE_TTL_MS;
43
+ constructor(logger: Logger, hailerClient?: HailerApiClient);
44
+ /**
45
+ * Invalidate cached balance for a workspace
46
+ * Call this after write operations that cost tokens
47
+ */
48
+ invalidateCache(workspaceId: string): void;
49
+ /**
50
+ * Check workspace balance before starting a new session
51
+ * Returns balance info - caller decides whether to reject
52
+ *
53
+ * Error handling: On network failure, returns a default "OK" response
54
+ * to allow graceful degradation (don't block users due to billing errors)
55
+ */
56
+ checkBalance(workspaceId: string): Promise<BalanceResponse>;
57
+ /**
58
+ * Fetch balance from Hailer backend API
59
+ * Separated from checkBalance to support deduplication of concurrent requests
60
+ */
61
+ private fetchBalanceFromBackend;
62
+ /**
63
+ * Burn tokens after LLM API call - fire and forget
64
+ * Does not block the response pipeline
65
+ *
66
+ * Error handling: Logs errors but does not throw
67
+ */
68
+ burnTokens(payload: TokenBurnPayload): Promise<void>;
69
+ /**
70
+ * Internal burn implementation with proper error handling
71
+ */
72
+ private burnTokensInternal;
73
+ /**
74
+ * Calculate cost in USD based on token counts and model
75
+ */
76
+ calculateCost(inputTokens: number, outputTokens: number, cacheCreationTokens: number, cacheReadTokens: number, model?: string): number;
77
+ }
78
+ //# sourceMappingURL=token-billing.d.ts.map
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ /**
3
+ * Token Billing Service
4
+ *
5
+ * Handles real-time token burning per workspace with balance checks at session start.
6
+ *
7
+ * Workspace Isolation:
8
+ * - Each workspace has its own token balance (managed by Hailer backend)
9
+ * - Balance check and burn calls include workspaceId for correct routing
10
+ *
11
+ * Behavior:
12
+ * - Check balance when new session starts -> reject if negative for that workspace
13
+ * - Burn tokens after each LLM API call -> real-time, charges correct workspace
14
+ * - Allow balance to go negative during active session -> don't interrupt
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.TokenBillingService = void 0;
18
+ // Claude Haiku 4.5 pricing (Oct 2025)
19
+ const HAIKU_PRICING = {
20
+ inputPerMillion: 1.00, // $1.00 per 1M input tokens
21
+ outputPerMillion: 5.00, // $5.00 per 1M output tokens
22
+ cacheWritePerMillion: 1.25, // $1.25 per 1M cache write tokens (1.25x input)
23
+ cacheReadPerMillion: 0.10, // $0.10 per 1M cache read tokens (0.1x input)
24
+ };
25
+ // Claude Sonnet 4.5 pricing (Sep 2025)
26
+ const SONNET_PRICING = {
27
+ inputPerMillion: 3.00, // $3.00 per 1M input tokens
28
+ outputPerMillion: 15.00, // $15.00 per 1M output tokens
29
+ cacheWritePerMillion: 3.75, // $3.75 per 1M cache write tokens (1.25x input)
30
+ cacheReadPerMillion: 0.30, // $0.30 per 1M cache read tokens (0.1x input)
31
+ };
32
+ const MODEL_PRICING = {
33
+ 'claude-haiku-4-5-20251001': HAIKU_PRICING,
34
+ 'claude-sonnet-4-5-20250929': SONNET_PRICING,
35
+ };
36
+ function getPricing(model) {
37
+ if (model) {
38
+ const match = MODEL_PRICING[model];
39
+ if (match)
40
+ return match;
41
+ }
42
+ return HAIKU_PRICING;
43
+ }
44
+ function getOutputWeights(pricing) {
45
+ return {
46
+ input: pricing.inputPerMillion / pricing.outputPerMillion,
47
+ output: 1.0,
48
+ cacheWrite: pricing.cacheWritePerMillion / pricing.outputPerMillion,
49
+ cacheRead: pricing.cacheReadPerMillion / pricing.outputPerMillion,
50
+ };
51
+ }
52
+ // ================================================================================
53
+ // SERVICE
54
+ // ================================================================================
55
+ class TokenBillingService {
56
+ logger;
57
+ hailerClient = null;
58
+ balanceCache = new Map();
59
+ pendingBalanceChecks = new Map();
60
+ static CACHE_TTL_MS = 60_000; // 60 seconds
61
+ constructor(logger, hailerClient) {
62
+ this.logger = logger;
63
+ this.hailerClient = hailerClient || null;
64
+ }
65
+ /**
66
+ * Invalidate cached balance for a workspace
67
+ * Call this after write operations that cost tokens
68
+ */
69
+ invalidateCache(workspaceId) {
70
+ this.balanceCache.delete(workspaceId);
71
+ this.logger.debug("Balance cache invalidated", { workspaceId });
72
+ }
73
+ /**
74
+ * Check workspace balance before starting a new session
75
+ * Returns balance info - caller decides whether to reject
76
+ *
77
+ * Error handling: On network failure, returns a default "OK" response
78
+ * to allow graceful degradation (don't block users due to billing errors)
79
+ */
80
+ async checkBalance(workspaceId) {
81
+ if (!this.hailerClient) {
82
+ this.logger.warn("Balance check skipped - no Hailer client");
83
+ // Allow processing when we can't check balance (graceful degradation)
84
+ return { workspaceId, balance: 999, isNegative: false, hasBalance: true };
85
+ }
86
+ // Check cache first
87
+ const cached = this.balanceCache.get(workspaceId);
88
+ if (cached && Date.now() - cached.timestamp < TokenBillingService.CACHE_TTL_MS) {
89
+ const balance = cached.balance;
90
+ const cacheAge = Math.round((Date.now() - cached.timestamp) / 1000);
91
+ // Determine status
92
+ let status;
93
+ if (balance < 0) {
94
+ status = 'NEGATIVE - SHOULD NOT RESPOND';
95
+ }
96
+ else if (balance === 0) {
97
+ status = 'EMPTY - SHOULD NOT RESPOND';
98
+ }
99
+ else if (balance < 5) {
100
+ status = 'LOW';
101
+ }
102
+ else {
103
+ status = 'OK';
104
+ }
105
+ this.logger.debug(`BALANCE CHECK (CACHED) | ${workspaceId} | ${balance} tokens | ${status}`);
106
+ return {
107
+ workspaceId,
108
+ balance,
109
+ isNegative: balance < 0,
110
+ hasBalance: balance > 0,
111
+ };
112
+ }
113
+ // Deduplicate concurrent balance checks for the same workspace
114
+ const pending = this.pendingBalanceChecks.get(workspaceId);
115
+ if (pending) {
116
+ this.logger.debug(`BALANCE CHECK (DEDUP) | ${workspaceId} | reusing in-flight request`);
117
+ return pending;
118
+ }
119
+ const promise = this.fetchBalanceFromBackend(workspaceId);
120
+ this.pendingBalanceChecks.set(workspaceId, promise);
121
+ try {
122
+ return await promise;
123
+ }
124
+ finally {
125
+ this.pendingBalanceChecks.delete(workspaceId);
126
+ }
127
+ }
128
+ /**
129
+ * Fetch balance from Hailer backend API
130
+ * Separated from checkBalance to support deduplication of concurrent requests
131
+ */
132
+ async fetchBalanceFromBackend(workspaceId) {
133
+ try {
134
+ const result = await this.hailerClient.request('v3.mcp.getTokenBalance', [workspaceId]);
135
+ const balance = typeof result?.balance === "number" ? result.balance :
136
+ typeof result === "number" ? result : 0;
137
+ // Cache the result
138
+ this.balanceCache.set(workspaceId, { balance, timestamp: Date.now() });
139
+ // Determine status
140
+ let status;
141
+ if (balance < 0) {
142
+ status = 'NEGATIVE - SHOULD NOT RESPOND';
143
+ }
144
+ else if (balance === 0) {
145
+ status = 'EMPTY - SHOULD NOT RESPOND';
146
+ }
147
+ else if (balance < 5) {
148
+ status = 'LOW';
149
+ }
150
+ else {
151
+ status = 'OK';
152
+ }
153
+ this.logger.debug(`BALANCE CHECK | ${workspaceId} | ${balance} tokens | ${status}`);
154
+ return {
155
+ workspaceId,
156
+ balance,
157
+ isNegative: balance < 0,
158
+ hasBalance: balance > 0,
159
+ };
160
+ }
161
+ catch (error) {
162
+ this.logger.warn(`BALANCE CHECK FAILED | ${workspaceId} | ${error instanceof Error ? error.message : String(error)}`);
163
+ return { workspaceId, balance: 0, isNegative: false, hasBalance: false };
164
+ }
165
+ }
166
+ /**
167
+ * Burn tokens after LLM API call - fire and forget
168
+ * Does not block the response pipeline
169
+ *
170
+ * Error handling: Logs errors but does not throw
171
+ */
172
+ async burnTokens(payload) {
173
+ // Fire and forget - don't await in caller
174
+ this.burnTokensInternal(payload).catch((error) => {
175
+ this.logger.error("Token burn failed", {
176
+ workspaceId: payload.workspaceId,
177
+ error: error instanceof Error ? error.message : String(error),
178
+ });
179
+ });
180
+ }
181
+ /**
182
+ * Internal burn implementation with proper error handling
183
+ */
184
+ async burnTokensInternal(payload) {
185
+ if (!this.hailerClient) {
186
+ this.logger.warn("Token burn skipped - no Hailer client");
187
+ return;
188
+ }
189
+ // Calculate output-normalized tokens (what backend expects)
190
+ const weights = getOutputWeights(getPricing(payload.model));
191
+ const normalizedTokens = Math.round(payload.inputTokens * weights.input +
192
+ payload.outputTokens * weights.output +
193
+ payload.cacheCreationTokens * weights.cacheWrite +
194
+ payload.cacheReadTokens * weights.cacheRead);
195
+ try {
196
+ const result = await this.hailerClient.request('v3.mcp.deductTokens', [{
197
+ mcpToken: 'hL9xK2mNpQrS4tVwY6zA8bCD',
198
+ workspaceId: payload.workspaceId,
199
+ tokenAmount: normalizedTokens,
200
+ model: 'claude-haiku',
201
+ meta: {
202
+ operation: 'chat'
203
+ }
204
+ }]);
205
+ // Cache the new balance from response
206
+ if (result?.newBalance !== undefined) {
207
+ this.balanceCache.set(payload.workspaceId, {
208
+ balance: result.newBalance,
209
+ timestamp: Date.now()
210
+ });
211
+ }
212
+ this.logger.debug(`TOKEN DEDUCTION | ${payload.workspaceId} | ${normalizedTokens} tokens | $${payload.costUsd.toFixed(6)} | balance: ${result?.newBalance ?? 'N/A'}`);
213
+ }
214
+ catch (error) {
215
+ this.logger.warn(`TOKEN DEDUCTION FAILED | ${payload.workspaceId} | ${normalizedTokens} tokens | ${error instanceof Error ? error.message : String(error)}`);
216
+ }
217
+ }
218
+ /**
219
+ * Calculate cost in USD based on token counts and model
220
+ */
221
+ calculateCost(inputTokens, outputTokens, cacheCreationTokens, cacheReadTokens, model) {
222
+ const pricing = getPricing(model);
223
+ const inputCost = (inputTokens / 1_000_000) * pricing.inputPerMillion;
224
+ const outputCost = (outputTokens / 1_000_000) * pricing.outputPerMillion;
225
+ const cacheWriteCost = (cacheCreationTokens / 1_000_000) * pricing.cacheWritePerMillion;
226
+ const cacheReadCost = (cacheReadTokens / 1_000_000) * pricing.cacheReadPerMillion;
227
+ const totalCost = inputCost + outputCost + cacheWriteCost + cacheReadCost;
228
+ // Round to 8 decimal places to avoid floating point issues
229
+ return Math.round(totalCost * 100_000_000) / 100_000_000;
230
+ }
231
+ }
232
+ exports.TokenBillingService = TokenBillingService;
233
+ //# sourceMappingURL=token-billing.js.map
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Bot Services Types
3
+ *
4
+ * Shared types for bot service layer.
5
+ * These are decoupled from the agent system's types.
6
+ */
7
+ /** Response from v3.discussion.message.latest */
8
+ export interface MessageLatestResponse {
9
+ messages: HailerMessage[];
10
+ }
11
+ /** Discussion info returned by messenger.load_discussions */
12
+ export interface DiscussionInfo {
13
+ _id: string;
14
+ participants: string[];
15
+ private?: boolean;
16
+ description?: string;
17
+ name?: string;
18
+ linked_activity?: string;
19
+ cid?: string;
20
+ }
21
+ /** Message data for messenger.send */
22
+ export interface SendMessageData {
23
+ msg: string;
24
+ links?: Array<{
25
+ target: string;
26
+ targetType: string;
27
+ type: string;
28
+ }>;
29
+ }
30
+ /** Generic bot connection interface - decoupled from BotClient */
31
+ export interface BotConnection {
32
+ client: {
33
+ socket: {
34
+ request: {
35
+ (method: 'v3.discussion.message.latest', args: [string]): Promise<MessageLatestResponse>;
36
+ (method: 'messenger.load_discussions', args: [string[]]): Promise<Record<string, DiscussionInfo>>;
37
+ (method: 'messenger.send', args: [SendMessageData, string]): Promise<void>;
38
+ (method: 'messenger.set_discussion_typing_state', args: [string, boolean]): Promise<void>;
39
+ (method: string, args: any[]): Promise<any>;
40
+ };
41
+ };
42
+ };
43
+ workspaceCache?: {
44
+ usersById: Record<string, {
45
+ id: string;
46
+ firstname?: string;
47
+ lastname?: string;
48
+ fullName?: string;
49
+ }>;
50
+ users: Array<{
51
+ id: string;
52
+ firstname?: string;
53
+ lastname?: string;
54
+ fullName?: string;
55
+ }>;
56
+ rawInit?: {
57
+ processes?: Array<{
58
+ _id: string;
59
+ name: string;
60
+ }>;
61
+ };
62
+ };
63
+ }
64
+ export type MessagePriority = "high" | "normal" | "low";
65
+ /** File attachment in a Hailer message */
66
+ export interface HailerFileAttachment {
67
+ _id: string;
68
+ filename?: string;
69
+ name?: string;
70
+ size?: number;
71
+ mime?: string;
72
+ type?: string;
73
+ }
74
+ /** Hailer discussion message from API */
75
+ export interface HailerMessage {
76
+ _id: string;
77
+ msg?: string;
78
+ content?: string;
79
+ uid: string;
80
+ userName?: string;
81
+ type?: string;
82
+ replyTo?: string;
83
+ created?: number;
84
+ files?: HailerFileAttachment[];
85
+ }
86
+ /** Hailer user from search/workspace cache */
87
+ export interface HailerUser {
88
+ _id: string;
89
+ firstname?: string;
90
+ lastname?: string;
91
+ fullName?: string;
92
+ email?: string;
93
+ }
94
+ /** File attachment info for incoming messages */
95
+ export interface IncomingFileAttachment {
96
+ fileId: string;
97
+ filename: string;
98
+ size?: number;
99
+ mime?: string;
100
+ }
101
+ export interface IncomingMessage {
102
+ id: string;
103
+ discussionId: string;
104
+ workspaceId: string;
105
+ discussionName?: string;
106
+ linkedActivityId?: string;
107
+ linkedActivityName?: string;
108
+ senderId: string;
109
+ senderName: string;
110
+ content: string;
111
+ timestamp: number;
112
+ priority: MessagePriority;
113
+ priorityReason: string;
114
+ isReplyToBot: boolean;
115
+ isMention: boolean;
116
+ isDirectMessage: boolean;
117
+ fileAttachments?: IncomingFileAttachment[];
118
+ }
119
+ export interface DiscussionState {
120
+ contextBuffer: IncomingMessage[];
121
+ abortController: AbortController | null;
122
+ state: 'idle' | 'engaged';
123
+ consecutiveNonResponses: number;
124
+ lastProgressTime: number;
125
+ }
126
+ export interface SessionMetrics {
127
+ inputTokens: number;
128
+ outputTokens: number;
129
+ cacheCreationTokens: number;
130
+ cacheReadTokens: number;
131
+ toolCalls: number;
132
+ writeOperations: number;
133
+ messagesProcessed: number;
134
+ responsesPosted: number;
135
+ apiCalls: number;
136
+ }
137
+ /** Detail about a write operation */
138
+ export interface WriteDetail {
139
+ tool: string;
140
+ what: string;
141
+ targetId?: string;
142
+ }
143
+ export interface ActivitySession {
144
+ activityId: string;
145
+ activityName: string;
146
+ discussionId: string;
147
+ workspaceId: string;
148
+ startTime: number;
149
+ lastActivityTime: number;
150
+ metrics: SessionMetrics;
151
+ actions: string[];
152
+ previousLogId: string | null;
153
+ conversation: string[];
154
+ writeDetails: WriteDetail[];
155
+ triggerRequest: string | null;
156
+ requestedBy: string | null;
157
+ requestedById: string | null;
158
+ }
159
+ export declare const SESSION_IDLE_TIMEOUT = 60000;
160
+ /** MCP tool result from server */
161
+ export interface McpToolResult {
162
+ content?: Array<{
163
+ text?: string;
164
+ type?: string;
165
+ }>;
166
+ }
167
+ /** Callback type for MCP tool calls */
168
+ export type McpToolCallback = (name: string, args: any) => Promise<McpToolResult>;
169
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ /**
3
+ * Bot Services Types
4
+ *
5
+ * Shared types for bot service layer.
6
+ * These are decoupled from the agent system's types.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.SESSION_IDLE_TIMEOUT = void 0;
10
+ // ===== CONSTANTS =====
11
+ exports.SESSION_IDLE_TIMEOUT = 60_000; // 60 seconds
12
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Typing Indicator Service
3
+ * Manages typing indicator state with auto-refresh for Hailer discussions
4
+ */
5
+ import { Logger } from "../../lib/logger";
6
+ import { BotConnection } from "./types";
7
+ export declare class TypingIndicatorService {
8
+ private botConnection;
9
+ private logger;
10
+ private static TYPING_REFRESH_MS;
11
+ private typingIntervals;
12
+ constructor(botConnection: BotConnection, logger: Logger);
13
+ /**
14
+ * Start typing indicator with auto-refresh interval for a discussion
15
+ */
16
+ start(discussionId: string): void;
17
+ /**
18
+ * Stop typing indicator for a specific discussion, or all discussions (shutdown)
19
+ */
20
+ stop(discussionId?: string): void;
21
+ private sendSignal;
22
+ }
23
+ //# sourceMappingURL=typing-indicator.d.ts.map
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ /**
3
+ * Typing Indicator Service
4
+ * Manages typing indicator state with auto-refresh for Hailer discussions
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.TypingIndicatorService = void 0;
8
+ class TypingIndicatorService {
9
+ botConnection;
10
+ logger;
11
+ static TYPING_REFRESH_MS = 3000;
12
+ typingIntervals = new Map();
13
+ constructor(botConnection, logger) {
14
+ this.botConnection = botConnection;
15
+ this.logger = logger;
16
+ }
17
+ /**
18
+ * Start typing indicator with auto-refresh interval for a discussion
19
+ */
20
+ start(discussionId) {
21
+ if (this.typingIntervals.has(discussionId))
22
+ return;
23
+ this.sendSignal(discussionId, true);
24
+ const interval = setInterval(() => {
25
+ this.sendSignal(discussionId, true);
26
+ }, TypingIndicatorService.TYPING_REFRESH_MS);
27
+ this.typingIntervals.set(discussionId, interval);
28
+ }
29
+ /**
30
+ * Stop typing indicator for a specific discussion, or all discussions (shutdown)
31
+ */
32
+ stop(discussionId) {
33
+ if (discussionId) {
34
+ const interval = this.typingIntervals.get(discussionId);
35
+ if (interval) {
36
+ clearInterval(interval);
37
+ this.typingIntervals.delete(discussionId);
38
+ this.sendSignal(discussionId, false);
39
+ }
40
+ }
41
+ else {
42
+ // Shutdown: clear all
43
+ for (const [discId, interval] of this.typingIntervals) {
44
+ clearInterval(interval);
45
+ this.sendSignal(discId, false);
46
+ }
47
+ this.typingIntervals.clear();
48
+ }
49
+ }
50
+ sendSignal(discussionId, isTyping) {
51
+ this.botConnection.client.socket.request("messenger.set_discussion_typing_state", [
52
+ discussionId,
53
+ isTyping
54
+ ]).catch((error) => {
55
+ this.logger.debug("Typing indicator failed", { discussionId, isTyping, error: error?.message });
56
+ });
57
+ }
58
+ }
59
+ exports.TypingIndicatorService = TypingIndicatorService;
60
+ //# sourceMappingURL=typing-indicator.js.map
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Workspace Schema Cache Service
3
+ *
4
+ * Dynamically discovers and caches workflow schemas per workspace.
5
+ * Replaces hardcoded workflow/field IDs with runtime lookup.
6
+ *
7
+ * WORKSPACE ISOLATION: Each workspace gets its own workflow IDs.
8
+ * Never use hardcoded IDs - always lookup via this cache.
9
+ *
10
+ * On bot startup:
11
+ * 1. Check memory cache first
12
+ * 2. If cache miss: Query list_workflows to find AI Agent workflows by name
13
+ * 3. If missing, install them from marketplace
14
+ * 4. Query get_workflow_schema to get field IDs
15
+ * 5. Store in memory cache for session duration
16
+ */
17
+ import { Logger } from "../../lib/logger";
18
+ import { McpToolCallback } from "./types";
19
+ /** Cached workflow schema */
20
+ export interface WorkflowSchema {
21
+ workflowId: string;
22
+ name: string;
23
+ phases: Record<string, string>;
24
+ fields: Record<string, string>;
25
+ }
26
+ /** Per-workspace cache */
27
+ export interface WorkspaceSchemas {
28
+ workspaceId: string;
29
+ sessionLog?: WorkflowSchema;
30
+ agentDirectory?: WorkflowSchema;
31
+ positions?: WorkflowSchema;
32
+ teams?: WorkflowSchema;
33
+ toolRegistry?: WorkflowSchema;
34
+ mcpConfig?: WorkflowSchema;
35
+ initialized: boolean;
36
+ }
37
+ export declare class WorkspaceSchemaCacheService {
38
+ private logger;
39
+ private callMcpTool;
40
+ private cache;
41
+ constructor(logger: Logger, callMcpTool: McpToolCallback);
42
+ /**
43
+ * Initialize schema cache for a workspace
44
+ * Call this on bot startup after connecting to Hailer
45
+ *
46
+ * Set SKIP_SCHEMA_DISCOVERY=true to skip API calls on startup.
47
+ * HAL can discover workflows on-demand via MCP tools when needed.
48
+ */
49
+ initializeForWorkspace(workspaceId: string): Promise<WorkspaceSchemas>;
50
+ /**
51
+ * Get cached schemas for a workspace
52
+ */
53
+ getSchemas(workspaceId: string): WorkspaceSchemas | undefined;
54
+ /**
55
+ * Get Session Log schema for a workspace
56
+ */
57
+ getSessionLogSchema(workspaceId: string): WorkflowSchema | undefined;
58
+ /**
59
+ * Get Agent Directory schema for a workspace
60
+ */
61
+ getAgentDirectorySchema(workspaceId: string): WorkflowSchema | undefined;
62
+ /**
63
+ * Get Positions schema for a workspace
64
+ */
65
+ getPositionsSchema(workspaceId: string): WorkflowSchema | undefined;
66
+ /**
67
+ * Get Teams schema for a workspace
68
+ */
69
+ getTeamsSchema(workspaceId: string): WorkflowSchema | undefined;
70
+ /**
71
+ * Get Tool Registry schema for a workspace
72
+ */
73
+ getToolRegistrySchema(workspaceId: string): WorkflowSchema | undefined;
74
+ /**
75
+ * Get MCP Config schema for a workspace
76
+ */
77
+ getMcpConfigSchema(workspaceId: string): WorkflowSchema | undefined;
78
+ /**
79
+ * Check if a workspace has the AI Agent template installed
80
+ */
81
+ hasAgentTemplate(workspaceId: string): boolean;
82
+ /**
83
+ * Check if workflow name matches a pattern (case insensitive, ignores emojis)
84
+ */
85
+ private matchesPattern;
86
+ /**
87
+ * Map workflow list to schema slots (using pattern matching)
88
+ */
89
+ private mapWorkflowsToSchemas;
90
+ /**
91
+ * Load detailed schemas (phases, fields) for all found workflows
92
+ */
93
+ private loadDetailedSchemas;
94
+ /**
95
+ * Install AI Agent template from marketplace
96
+ */
97
+ private installAgentTemplate;
98
+ /**
99
+ * Parse workflows from list_workflows response
100
+ * Handles both raw JSON and markdown-wrapped JSON responses
101
+ */
102
+ private parseWorkflows;
103
+ /**
104
+ * Load detailed workflow schema (phases and fields) for an existing schema
105
+ */
106
+ private loadWorkflowSchemaDetails;
107
+ /**
108
+ * Parse phases from list_workflow_phases response
109
+ * Format: 1. **PhaseName**\n - Phase ID: `phaseId`
110
+ */
111
+ private parsePhases;
112
+ /**
113
+ * Parse fields from get_workflow_schema response
114
+ * Stores fields by both label and key for flexible lookup
115
+ */
116
+ private parseFields;
117
+ /**
118
+ * Clear cache for a workspace (memory cache only)
119
+ */
120
+ clearCache(workspaceId?: string): void;
121
+ }
122
+ //# sourceMappingURL=workspace-schema-cache.d.ts.map