@hailer/mcp 1.2.0 → 2.0.0-beta.1
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.
- package/.claude/agents/agent-hailer-helper.md +118 -0
- package/.claude/commands/app-squad.md +16 -110
- package/.claude/commands/debug-squad.md +13 -290
- package/.claude/commands/publish.md +2 -2
- package/.claude/commands/review-squad.md +17 -139
- package/.claude/skills/create-and-publish-app/SKILL.md +102 -83
- package/.claude/skills/hailer-app-builder/SKILL.md +2 -2
- package/.claude/skills/hailer-ui-guide/SKILL.md +265 -0
- package/.env.example +50 -1
- package/CLAUDE.md +136 -10
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +3 -0
- package/dist/app.js.map +1 -1
- package/dist/bot/bot-manager.d.ts +9 -6
- package/dist/bot/bot-manager.d.ts.map +1 -1
- package/dist/bot/bot-manager.js +142 -31
- package/dist/bot/bot-manager.js.map +1 -1
- package/dist/bot/bot.d.ts +59 -16
- package/dist/bot/bot.d.ts.map +1 -1
- package/dist/bot/bot.js +889 -142
- package/dist/bot/bot.js.map +1 -1
- package/dist/bot/operation-logger.d.ts.map +1 -1
- package/dist/bot/operation-logger.js +24 -12
- package/dist/bot/operation-logger.js.map +1 -1
- package/dist/bot/services/bot-permissions.d.ts +2 -2
- package/dist/bot/services/bot-permissions.d.ts.map +1 -1
- package/dist/bot/services/bot-permissions.js +28 -9
- package/dist/bot/services/bot-permissions.js.map +1 -1
- package/dist/bot/services/conversation-manager.d.ts +23 -23
- package/dist/bot/services/conversation-manager.d.ts.map +1 -1
- package/dist/bot/services/conversation-manager.js +52 -49
- package/dist/bot/services/conversation-manager.js.map +1 -1
- package/dist/bot/services/helper-prompt.d.ts +8 -0
- package/dist/bot/services/helper-prompt.d.ts.map +1 -0
- package/dist/bot/services/helper-prompt.js +177 -0
- package/dist/bot/services/helper-prompt.js.map +1 -0
- package/dist/bot/services/message-classifier.d.ts +16 -16
- package/dist/bot/services/message-classifier.d.ts.map +1 -1
- package/dist/bot/services/message-classifier.js +55 -49
- package/dist/bot/services/message-classifier.js.map +1 -1
- package/dist/bot/services/message-formatter.d.ts +38 -38
- package/dist/bot/services/message-formatter.d.ts.map +1 -1
- package/dist/bot/services/message-formatter.js +81 -74
- package/dist/bot/services/message-formatter.js.map +1 -1
- package/dist/bot/services/permission-guard.d.ts.map +1 -1
- package/dist/bot/services/permission-guard.js +20 -10
- package/dist/bot/services/permission-guard.js.map +1 -1
- package/dist/bot/services/signal-router.d.ts.map +1 -1
- package/dist/bot/services/signal-router.js +11 -6
- package/dist/bot/services/signal-router.js.map +1 -1
- package/dist/bot/services/system-prompt.d.ts +14 -0
- package/dist/bot/services/system-prompt.d.ts.map +1 -1
- package/dist/bot/services/system-prompt.js +179 -4
- package/dist/bot/services/system-prompt.js.map +1 -1
- package/dist/bot/services/token-billing.d.ts +23 -23
- package/dist/bot/services/token-billing.d.ts.map +1 -1
- package/dist/bot/services/token-billing.js +51 -36
- package/dist/bot/services/token-billing.js.map +1 -1
- package/dist/bot/services/types.d.ts +3 -1
- package/dist/bot/services/types.d.ts.map +1 -1
- package/dist/bot/services/typing-indicator.d.ts +8 -8
- package/dist/bot/services/typing-indicator.d.ts.map +1 -1
- package/dist/bot/services/typing-indicator.js +12 -10
- package/dist/bot/services/typing-indicator.js.map +1 -1
- package/dist/bot/services/workspace-refresh.d.ts +3 -3
- package/dist/bot/services/workspace-refresh.d.ts.map +1 -1
- package/dist/bot/services/workspace-refresh.js +23 -13
- package/dist/bot/services/workspace-refresh.js.map +1 -1
- package/dist/bot/tool-executor.d.ts +10 -6
- package/dist/bot/tool-executor.d.ts.map +1 -1
- package/dist/bot/tool-executor.js +12 -6
- package/dist/bot/tool-executor.js.map +1 -1
- package/dist/bot/workspace-overview.d.ts.map +1 -1
- package/dist/bot/workspace-overview.js +6 -3
- package/dist/bot/workspace-overview.js.map +1 -1
- package/dist/bot-config/activity-error.d.ts +47 -0
- package/dist/bot-config/activity-error.d.ts.map +1 -0
- package/dist/bot-config/activity-error.js +67 -0
- package/dist/bot-config/activity-error.js.map +1 -0
- package/dist/bot-config/context.d.ts +4 -4
- package/dist/bot-config/context.d.ts.map +1 -1
- package/dist/bot-config/context.js +18 -14
- package/dist/bot-config/context.js.map +1 -1
- package/dist/bot-config/events.d.ts +45 -0
- package/dist/bot-config/events.d.ts.map +1 -0
- package/dist/bot-config/events.js +51 -0
- package/dist/bot-config/events.js.map +1 -0
- package/dist/bot-config/index.d.ts +3 -0
- package/dist/bot-config/index.d.ts.map +1 -1
- package/dist/bot-config/index.js +8 -1
- package/dist/bot-config/index.js.map +1 -1
- package/dist/bot-config/loader.d.ts +3 -0
- package/dist/bot-config/loader.d.ts.map +1 -1
- package/dist/bot-config/loader.js +45 -20
- package/dist/bot-config/loader.js.map +1 -1
- package/dist/bot-config/persistence.js.map +1 -1
- package/dist/bot-config/reconciler.d.ts +11 -0
- package/dist/bot-config/reconciler.d.ts.map +1 -0
- package/dist/bot-config/reconciler.js +121 -0
- package/dist/bot-config/reconciler.js.map +1 -0
- package/dist/bot-config/state.d.ts.map +1 -1
- package/dist/bot-config/state.js.map +1 -1
- package/dist/bot-config/types.d.ts +32 -0
- package/dist/bot-config/types.d.ts.map +1 -1
- package/dist/bot-config/webhooks.d.ts.map +1 -1
- package/dist/bot-config/webhooks.js.map +1 -1
- package/dist/bot-config/workflow-installer.d.ts +37 -0
- package/dist/bot-config/workflow-installer.d.ts.map +1 -0
- package/dist/bot-config/workflow-installer.js +346 -0
- package/dist/bot-config/workflow-installer.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +12 -0
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +23 -19
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +65 -27
- package/dist/config.js.map +1 -1
- package/dist/core.d.ts +6 -4
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +11 -16
- package/dist/core.js.map +1 -1
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +7 -4
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/request-logger.d.ts +19 -19
- package/dist/lib/request-logger.d.ts.map +1 -1
- package/dist/lib/request-logger.js +19 -19
- package/dist/lib/request-logger.js.map +1 -1
- package/dist/mcp/UserContextCache.d.ts +28 -22
- package/dist/mcp/UserContextCache.d.ts.map +1 -1
- package/dist/mcp/UserContextCache.js +23 -23
- package/dist/mcp/UserContextCache.js.map +1 -1
- package/dist/mcp/auth.js.map +1 -1
- package/dist/mcp/hailer-clients.d.ts +5 -4
- package/dist/mcp/hailer-clients.d.ts.map +1 -1
- package/dist/mcp/hailer-clients.js +61 -27
- package/dist/mcp/hailer-clients.js.map +1 -1
- package/dist/mcp/hailer-rpc.d.ts +40 -0
- package/dist/mcp/hailer-rpc.d.ts.map +1 -0
- package/dist/mcp/hailer-rpc.js +43 -0
- package/dist/mcp/hailer-rpc.js.map +1 -0
- package/dist/mcp/session-store.d.ts +16 -16
- package/dist/mcp/session-store.d.ts.map +1 -1
- package/dist/mcp/session-store.js +16 -16
- package/dist/mcp/session-store.js.map +1 -1
- package/dist/mcp/tool-profiles.d.ts +69 -0
- package/dist/mcp/tool-profiles.d.ts.map +1 -0
- package/dist/mcp/tool-profiles.js +176 -0
- package/dist/mcp/tool-profiles.js.map +1 -0
- package/dist/mcp/tool-registry.d.ts +16 -0
- package/dist/mcp/tool-registry.d.ts.map +1 -1
- package/dist/mcp/tool-registry.js +91 -39
- package/dist/mcp/tool-registry.js.map +1 -1
- package/dist/mcp/tools/activity.d.ts.map +1 -1
- package/dist/mcp/tools/activity.js +398 -198
- package/dist/mcp/tools/activity.js.map +1 -1
- package/dist/mcp/tools/aliases.d.ts +11 -0
- package/dist/mcp/tools/aliases.d.ts.map +1 -0
- package/dist/mcp/tools/aliases.js +176 -0
- package/dist/mcp/tools/aliases.js.map +1 -0
- package/dist/mcp/tools/app-core.d.ts +6 -8
- package/dist/mcp/tools/app-core.d.ts.map +1 -1
- package/dist/mcp/tools/app-core.js +355 -254
- package/dist/mcp/tools/app-core.js.map +1 -1
- package/dist/mcp/tools/app-marketplace.d.ts +8 -16
- package/dist/mcp/tools/app-marketplace.d.ts.map +1 -1
- package/dist/mcp/tools/app-marketplace.js +604 -930
- package/dist/mcp/tools/app-marketplace.js.map +1 -1
- package/dist/mcp/tools/app.d.ts +4 -7
- package/dist/mcp/tools/app.d.ts.map +1 -1
- package/dist/mcp/tools/app.js +4 -7
- package/dist/mcp/tools/app.js.map +1 -1
- package/dist/mcp/tools/bot-self.d.ts +21 -0
- package/dist/mcp/tools/bot-self.d.ts.map +1 -0
- package/dist/mcp/tools/bot-self.js +174 -0
- package/dist/mcp/tools/bot-self.js.map +1 -0
- package/dist/mcp/tools/calendar.d.ts +21 -0
- package/dist/mcp/tools/calendar.d.ts.map +1 -0
- package/dist/mcp/tools/calendar.js +741 -0
- package/dist/mcp/tools/calendar.js.map +1 -0
- package/dist/mcp/tools/company.d.ts.map +1 -1
- package/dist/mcp/tools/company.js +2 -1
- package/dist/mcp/tools/company.js.map +1 -1
- package/dist/mcp/tools/date.js.map +1 -1
- package/dist/mcp/tools/discussion.d.ts +23 -3
- package/dist/mcp/tools/discussion.d.ts.map +1 -1
- package/dist/mcp/tools/discussion.js +417 -534
- package/dist/mcp/tools/discussion.js.map +1 -1
- package/dist/mcp/tools/file.d.ts.map +1 -1
- package/dist/mcp/tools/file.js +18 -16
- package/dist/mcp/tools/file.js.map +1 -1
- package/dist/mcp/tools/index.js +4 -4
- package/dist/mcp/tools/index.js.map +1 -1
- package/dist/mcp/tools/insight.d.ts +7 -5
- package/dist/mcp/tools/insight.d.ts.map +1 -1
- package/dist/mcp/tools/insight.js +419 -477
- package/dist/mcp/tools/insight.js.map +1 -1
- package/dist/mcp/tools/user.d.ts.map +1 -1
- package/dist/mcp/tools/user.js +15 -13
- package/dist/mcp/tools/user.js.map +1 -1
- package/dist/mcp/tools/workflow-permissions.d.ts +2 -4
- package/dist/mcp/tools/workflow-permissions.d.ts.map +1 -1
- package/dist/mcp/tools/workflow-permissions.js +88 -97
- package/dist/mcp/tools/workflow-permissions.js.map +1 -1
- package/dist/mcp/tools/workflow.d.ts +2 -7
- package/dist/mcp/tools/workflow.d.ts.map +1 -1
- package/dist/mcp/tools/workflow.js +817 -850
- package/dist/mcp/tools/workflow.js.map +1 -1
- package/dist/mcp/utils/api-errors.d.ts.map +1 -1
- package/dist/mcp/utils/api-errors.js +2 -2
- package/dist/mcp/utils/api-errors.js.map +1 -1
- package/dist/mcp/utils/data-transformers.d.ts.map +1 -1
- package/dist/mcp/utils/data-transformers.js +8 -4
- package/dist/mcp/utils/data-transformers.js.map +1 -1
- package/dist/mcp/utils/file-upload.d.ts.map +1 -1
- package/dist/mcp/utils/file-upload.js +1 -1
- package/dist/mcp/utils/file-upload.js.map +1 -1
- package/dist/mcp/utils/hailer-api-client.d.ts +81 -81
- package/dist/mcp/utils/hailer-api-client.d.ts.map +1 -1
- package/dist/mcp/utils/hailer-api-client.js +103 -101
- package/dist/mcp/utils/hailer-api-client.js.map +1 -1
- package/dist/mcp/utils/index.d.ts.map +1 -1
- package/dist/mcp/utils/index.js.map +1 -1
- package/dist/mcp/utils/logger.d.ts.map +1 -1
- package/dist/mcp/utils/logger.js.map +1 -1
- package/dist/mcp/utils/response-builder.d.ts.map +1 -1
- package/dist/mcp/utils/response-builder.js +8 -4
- package/dist/mcp/utils/response-builder.js.map +1 -1
- package/dist/mcp/utils/role-utils.d.ts.map +1 -1
- package/dist/mcp/utils/role-utils.js +6 -3
- package/dist/mcp/utils/role-utils.js.map +1 -1
- package/dist/mcp/utils/tool-helpers.d.ts.map +1 -1
- package/dist/mcp/utils/tool-helpers.js +2 -2
- package/dist/mcp/utils/tool-helpers.js.map +1 -1
- package/dist/mcp/utils/types.d.ts +1 -1
- package/dist/mcp/utils/types.d.ts.map +1 -1
- package/dist/mcp/utils/types.js.map +1 -1
- package/dist/mcp/webhook-handler.d.ts +43 -8
- package/dist/mcp/webhook-handler.d.ts.map +1 -1
- package/dist/mcp/webhook-handler.js +861 -116
- package/dist/mcp/webhook-handler.js.map +1 -1
- package/dist/mcp/workspace-admin-store.d.ts +49 -0
- package/dist/mcp/workspace-admin-store.d.ts.map +1 -0
- package/dist/mcp/workspace-admin-store.js +168 -0
- package/dist/mcp/workspace-admin-store.js.map +1 -0
- package/dist/mcp/workspace-cache.d.ts +2 -2
- package/dist/mcp/workspace-cache.d.ts.map +1 -1
- package/dist/mcp/workspace-cache.js +9 -5
- package/dist/mcp/workspace-cache.js.map +1 -1
- package/dist/mcp-server.d.ts +26 -11
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +360 -36
- package/dist/mcp-server.js.map +1 -1
- package/dist/plugins/vipunen/client.d.ts +41 -41
- package/dist/plugins/vipunen/client.d.ts.map +1 -1
- package/dist/plugins/vipunen/client.js +53 -48
- package/dist/plugins/vipunen/client.js.map +1 -1
- package/dist/plugins/vipunen/index.js.map +1 -1
- package/dist/plugins/vipunen/tools.d.ts.map +1 -1
- package/dist/plugins/vipunen/tools.js +6 -3
- package/dist/plugins/vipunen/tools.js.map +1 -1
- package/dist/public-chat/graduate.d.ts +29 -0
- package/dist/public-chat/graduate.d.ts.map +1 -0
- package/dist/public-chat/graduate.js +593 -0
- package/dist/public-chat/graduate.js.map +1 -0
- package/dist/public-chat/handler.d.ts +12 -0
- package/dist/public-chat/handler.d.ts.map +1 -0
- package/dist/public-chat/handler.js +179 -0
- package/dist/public-chat/handler.js.map +1 -0
- package/dist/public-chat/index.d.ts +16 -0
- package/dist/public-chat/index.d.ts.map +1 -0
- package/dist/public-chat/index.js +74 -0
- package/dist/public-chat/index.js.map +1 -0
- package/dist/public-chat/knowledge.d.ts +3 -0
- package/dist/public-chat/knowledge.d.ts.map +1 -0
- package/dist/public-chat/knowledge.js +1339 -0
- package/dist/public-chat/knowledge.js.map +1 -0
- package/dist/public-chat/rate-limit.d.ts +16 -0
- package/dist/public-chat/rate-limit.d.ts.map +1 -0
- package/dist/public-chat/rate-limit.js +51 -0
- package/dist/public-chat/rate-limit.js.map +1 -0
- package/dist/public-chat/session-store.d.ts +41 -0
- package/dist/public-chat/session-store.d.ts.map +1 -0
- package/dist/public-chat/session-store.js +95 -0
- package/dist/public-chat/session-store.js.map +1 -0
- package/dist/public-chat/studio-prewarm.d.ts +61 -0
- package/dist/public-chat/studio-prewarm.d.ts.map +1 -0
- package/dist/public-chat/studio-prewarm.js +162 -0
- package/dist/public-chat/studio-prewarm.js.map +1 -0
- package/dist/public-chat/system-prompt.d.ts +22 -0
- package/dist/public-chat/system-prompt.d.ts.map +1 -0
- package/dist/public-chat/system-prompt.js +428 -0
- package/dist/public-chat/system-prompt.js.map +1 -0
- package/package.json +14 -6
- package/scripts/build-public-chat-knowledge.py +101 -0
- package/scripts/probe-mcp-pricing.ts +52 -0
- package/scripts/smoke-public-chat-live.ts +148 -0
- package/scripts/smoke-public-chat.ts +110 -0
- package/.claude/CLAUDE.md +0 -126
- package/.claude/commands/audit-squad.md +0 -158
- package/.claude/commands/cleanup-squad.md +0 -98
- package/.claude/commands/config-squad.md +0 -106
- package/.claude/commands/crud-squad.md +0 -87
- package/.claude/commands/data-squad.md +0 -97
- package/.claude/commands/doc-squad.md +0 -65
- package/.claude/commands/help.md +0 -29
- package/.claude/commands/help:agents.md +0 -182
- package/.claude/commands/help:commands.md +0 -78
- package/.claude/commands/help:faq.md +0 -79
- package/.claude/commands/help:plugins.md +0 -50
- package/.claude/commands/help:skills.md +0 -87
- package/.claude/commands/help:tools.md +0 -75
- package/.claude/commands/hotfix-squad.md +0 -112
- package/.claude/commands/integration-squad.md +0 -82
- package/.claude/commands/janitor-squad.md +0 -167
- package/.claude/commands/onboard-squad.md +0 -130
- package/.claude/commands/swarm.md +0 -210
- package/.claude/commands/tool-builder.md +0 -39
- package/.claude/skills/publish-hailer-app/SKILL.md +0 -280
- package/dist/CLAUDE.md +0 -370
- package/dist/agents/bot-manager.d.ts +0 -48
- package/dist/agents/bot-manager.d.ts.map +0 -1
- package/dist/agents/bot-manager.js +0 -254
- package/dist/agents/bot-manager.js.map +0 -1
- package/dist/agents/bug-fixer/ai.d.ts +0 -80
- package/dist/agents/bug-fixer/ai.d.ts.map +0 -1
- package/dist/agents/bug-fixer/ai.js +0 -466
- package/dist/agents/bug-fixer/ai.js.map +0 -1
- package/dist/agents/bug-fixer/bot.d.ts +0 -92
- package/dist/agents/bug-fixer/bot.d.ts.map +0 -1
- package/dist/agents/bug-fixer/bot.js +0 -687
- package/dist/agents/bug-fixer/bot.js.map +0 -1
- package/dist/agents/bug-fixer/config.d.ts +0 -21
- package/dist/agents/bug-fixer/config.d.ts.map +0 -1
- package/dist/agents/bug-fixer/config.js +0 -218
- package/dist/agents/bug-fixer/config.js.map +0 -1
- package/dist/agents/bug-fixer/files.d.ts +0 -67
- package/dist/agents/bug-fixer/files.d.ts.map +0 -1
- package/dist/agents/bug-fixer/files.js +0 -386
- package/dist/agents/bug-fixer/files.js.map +0 -1
- package/dist/agents/bug-fixer/git.d.ts +0 -48
- package/dist/agents/bug-fixer/git.d.ts.map +0 -1
- package/dist/agents/bug-fixer/git.js +0 -298
- package/dist/agents/bug-fixer/git.js.map +0 -1
- package/dist/agents/bug-fixer/index.d.ts +0 -103
- package/dist/agents/bug-fixer/index.d.ts.map +0 -1
- package/dist/agents/bug-fixer/index.js +0 -262
- package/dist/agents/bug-fixer/index.js.map +0 -1
- package/dist/agents/bug-fixer/lsp.d.ts +0 -113
- package/dist/agents/bug-fixer/lsp.d.ts.map +0 -1
- package/dist/agents/bug-fixer/lsp.js +0 -485
- package/dist/agents/bug-fixer/lsp.js.map +0 -1
- package/dist/agents/bug-fixer/monitor.d.ts +0 -123
- package/dist/agents/bug-fixer/monitor.d.ts.map +0 -1
- package/dist/agents/bug-fixer/monitor.js +0 -629
- package/dist/agents/bug-fixer/monitor.js.map +0 -1
- package/dist/agents/bug-fixer/prompt.d.ts +0 -5
- package/dist/agents/bug-fixer/prompt.d.ts.map +0 -1
- package/dist/agents/bug-fixer/prompt.js +0 -94
- package/dist/agents/bug-fixer/prompt.js.map +0 -1
- package/dist/agents/bug-fixer/registries/pending-classification.d.ts +0 -28
- package/dist/agents/bug-fixer/registries/pending-classification.d.ts.map +0 -1
- package/dist/agents/bug-fixer/registries/pending-classification.js +0 -50
- package/dist/agents/bug-fixer/registries/pending-classification.js.map +0 -1
- package/dist/agents/bug-fixer/registries/pending-fix.d.ts +0 -33
- package/dist/agents/bug-fixer/registries/pending-fix.d.ts.map +0 -1
- package/dist/agents/bug-fixer/registries/pending-fix.js +0 -64
- package/dist/agents/bug-fixer/registries/pending-fix.js.map +0 -1
- package/dist/agents/bug-fixer/registries/pending.d.ts +0 -27
- package/dist/agents/bug-fixer/registries/pending.d.ts.map +0 -1
- package/dist/agents/bug-fixer/registries/pending.js +0 -49
- package/dist/agents/bug-fixer/registries/pending.js.map +0 -1
- package/dist/agents/bug-fixer/specialist-daemon.d.ts +0 -88
- package/dist/agents/bug-fixer/specialist-daemon.d.ts.map +0 -1
- package/dist/agents/bug-fixer/specialist-daemon.js +0 -431
- package/dist/agents/bug-fixer/specialist-daemon.js.map +0 -1
- package/dist/agents/bug-fixer/specialist.d.ts +0 -47
- package/dist/agents/bug-fixer/specialist.d.ts.map +0 -1
- package/dist/agents/bug-fixer/specialist.js +0 -327
- package/dist/agents/bug-fixer/specialist.js.map +0 -1
- package/dist/agents/bug-fixer/types.d.ts +0 -123
- package/dist/agents/bug-fixer/types.d.ts.map +0 -1
- package/dist/agents/bug-fixer/types.js +0 -9
- package/dist/agents/bug-fixer/types.js.map +0 -1
- package/dist/agents/factory.d.ts +0 -172
- package/dist/agents/factory.d.ts.map +0 -1
- package/dist/agents/factory.js +0 -706
- package/dist/agents/factory.js.map +0 -1
- package/dist/agents/hailer-expert/index.d.ts +0 -8
- package/dist/agents/hailer-expert/index.d.ts.map +0 -1
- package/dist/agents/hailer-expert/index.js +0 -14
- package/dist/agents/hailer-expert/index.js.map +0 -1
- package/dist/agents/hal/daemon.d.ts +0 -174
- package/dist/agents/hal/daemon.d.ts.map +0 -1
- package/dist/agents/hal/daemon.js +0 -1385
- package/dist/agents/hal/daemon.js.map +0 -1
- package/dist/agents/hal/definitions.d.ts +0 -42
- package/dist/agents/hal/definitions.d.ts.map +0 -1
- package/dist/agents/hal/definitions.js +0 -300
- package/dist/agents/hal/definitions.js.map +0 -1
- package/dist/agents/hal/index.d.ts +0 -3
- package/dist/agents/hal/index.d.ts.map +0 -1
- package/dist/agents/hal/index.js +0 -8
- package/dist/agents/hal/index.js.map +0 -1
- package/dist/agents/index.d.ts +0 -18
- package/dist/agents/index.d.ts.map +0 -1
- package/dist/agents/index.js +0 -48
- package/dist/agents/index.js.map +0 -1
- package/dist/agents/shared/base.d.ts +0 -253
- package/dist/agents/shared/base.d.ts.map +0 -1
- package/dist/agents/shared/base.js +0 -1122
- package/dist/agents/shared/base.js.map +0 -1
- package/dist/agents/shared/schemas/action-schema.d.ts +0 -62
- package/dist/agents/shared/schemas/action-schema.d.ts.map +0 -1
- package/dist/agents/shared/schemas/action-schema.js +0 -483
- package/dist/agents/shared/schemas/action-schema.js.map +0 -1
- package/dist/agents/shared/services/agent-registry.d.ts +0 -108
- package/dist/agents/shared/services/agent-registry.d.ts.map +0 -1
- package/dist/agents/shared/services/agent-registry.js +0 -469
- package/dist/agents/shared/services/agent-registry.js.map +0 -1
- package/dist/agents/shared/services/conversation-manager.d.ts +0 -57
- package/dist/agents/shared/services/conversation-manager.d.ts.map +0 -1
- package/dist/agents/shared/services/conversation-manager.js +0 -168
- package/dist/agents/shared/services/conversation-manager.js.map +0 -1
- package/dist/agents/shared/services/mcp-client.d.ts +0 -56
- package/dist/agents/shared/services/mcp-client.d.ts.map +0 -1
- package/dist/agents/shared/services/mcp-client.js +0 -124
- package/dist/agents/shared/services/mcp-client.js.map +0 -1
- package/dist/agents/shared/services/message-classifier.d.ts +0 -37
- package/dist/agents/shared/services/message-classifier.d.ts.map +0 -1
- package/dist/agents/shared/services/message-classifier.js +0 -203
- package/dist/agents/shared/services/message-classifier.js.map +0 -1
- package/dist/agents/shared/services/message-formatter.d.ts +0 -89
- package/dist/agents/shared/services/message-formatter.d.ts.map +0 -1
- package/dist/agents/shared/services/message-formatter.js +0 -390
- package/dist/agents/shared/services/message-formatter.js.map +0 -1
- package/dist/agents/shared/services/session-logger.d.ts +0 -162
- package/dist/agents/shared/services/session-logger.d.ts.map +0 -1
- package/dist/agents/shared/services/session-logger.js +0 -724
- package/dist/agents/shared/services/session-logger.js.map +0 -1
- package/dist/agents/shared/services/structured-output-executor.d.ts +0 -88
- package/dist/agents/shared/services/structured-output-executor.d.ts.map +0 -1
- package/dist/agents/shared/services/structured-output-executor.js +0 -296
- package/dist/agents/shared/services/structured-output-executor.js.map +0 -1
- package/dist/agents/shared/services/token-billing.d.ts +0 -72
- package/dist/agents/shared/services/token-billing.d.ts.map +0 -1
- package/dist/agents/shared/services/token-billing.js +0 -198
- package/dist/agents/shared/services/token-billing.js.map +0 -1
- package/dist/agents/shared/services/tool-executor.d.ts +0 -43
- package/dist/agents/shared/services/tool-executor.d.ts.map +0 -1
- package/dist/agents/shared/services/tool-executor.js +0 -175
- package/dist/agents/shared/services/tool-executor.js.map +0 -1
- package/dist/agents/shared/services/typing-indicator.d.ts +0 -24
- package/dist/agents/shared/services/typing-indicator.d.ts.map +0 -1
- package/dist/agents/shared/services/typing-indicator.js +0 -54
- package/dist/agents/shared/services/typing-indicator.js.map +0 -1
- package/dist/agents/shared/services/workspace-schema-cache.d.ts +0 -122
- package/dist/agents/shared/services/workspace-schema-cache.d.ts.map +0 -1
- package/dist/agents/shared/services/workspace-schema-cache.js +0 -507
- package/dist/agents/shared/services/workspace-schema-cache.js.map +0 -1
- package/dist/agents/shared/specialist.d.ts +0 -91
- package/dist/agents/shared/specialist.d.ts.map +0 -1
- package/dist/agents/shared/specialist.js +0 -399
- package/dist/agents/shared/specialist.js.map +0 -1
- package/dist/agents/shared/tool-schema-loader.d.ts +0 -65
- package/dist/agents/shared/tool-schema-loader.d.ts.map +0 -1
- package/dist/agents/shared/tool-schema-loader.js +0 -238
- package/dist/agents/shared/tool-schema-loader.js.map +0 -1
- package/dist/agents/shared/types.d.ts +0 -190
- package/dist/agents/shared/types.d.ts.map +0 -1
- package/dist/agents/shared/types.js +0 -13
- package/dist/agents/shared/types.js.map +0 -1
- package/dist/bot/bot-config.d.ts +0 -37
- package/dist/bot/bot-config.d.ts.map +0 -1
- package/dist/bot/bot-config.js +0 -219
- package/dist/bot/bot-config.js.map +0 -1
- package/dist/bot/services/__tests__/permission-guard.test.d.ts +0 -2
- package/dist/bot/services/__tests__/permission-guard.test.d.ts.map +0 -1
- package/dist/bot/services/__tests__/permission-guard.test.js +0 -357
- package/dist/bot/services/__tests__/permission-guard.test.js.map +0 -1
- package/dist/bot/services/session-logger.d.ts +0 -162
- package/dist/bot/services/session-logger.d.ts.map +0 -1
- package/dist/bot/services/session-logger.js +0 -724
- package/dist/bot/services/session-logger.js.map +0 -1
- package/dist/bot/services/workspace-schema-cache.d.ts +0 -122
- package/dist/bot/services/workspace-schema-cache.d.ts.map +0 -1
- package/dist/bot/services/workspace-schema-cache.js +0 -506
- package/dist/bot/services/workspace-schema-cache.js.map +0 -1
- package/dist/bot-config/tools.d.ts +0 -28
- package/dist/bot-config/tools.d.ts.map +0 -1
- package/dist/bot-config/tools.js +0 -279
- package/dist/bot-config/tools.js.map +0 -1
- package/dist/client/agents/base.d.ts +0 -207
- package/dist/client/agents/base.d.ts.map +0 -1
- package/dist/client/agents/base.js +0 -744
- package/dist/client/agents/base.js.map +0 -1
- package/dist/client/agents/definitions.d.ts +0 -53
- package/dist/client/agents/definitions.d.ts.map +0 -1
- package/dist/client/agents/definitions.js +0 -263
- package/dist/client/agents/definitions.js.map +0 -1
- package/dist/client/agents/orchestrator.d.ts +0 -141
- package/dist/client/agents/orchestrator.d.ts.map +0 -1
- package/dist/client/agents/orchestrator.js +0 -1062
- package/dist/client/agents/orchestrator.js.map +0 -1
- package/dist/client/agents/specialist.d.ts +0 -86
- package/dist/client/agents/specialist.d.ts.map +0 -1
- package/dist/client/agents/specialist.js +0 -340
- package/dist/client/agents/specialist.js.map +0 -1
- package/dist/client/bot-entrypoint.d.ts +0 -7
- package/dist/client/bot-entrypoint.d.ts.map +0 -1
- package/dist/client/bot-entrypoint.js +0 -103
- package/dist/client/bot-entrypoint.js.map +0 -1
- package/dist/client/bot-manager.d.ts +0 -44
- package/dist/client/bot-manager.d.ts.map +0 -1
- package/dist/client/bot-manager.js +0 -173
- package/dist/client/bot-manager.js.map +0 -1
- package/dist/client/bot-runner.d.ts +0 -35
- package/dist/client/bot-runner.d.ts.map +0 -1
- package/dist/client/bot-runner.js +0 -188
- package/dist/client/bot-runner.js.map +0 -1
- package/dist/client/chat-agent-daemon.d.ts +0 -464
- package/dist/client/chat-agent-daemon.d.ts.map +0 -1
- package/dist/client/chat-agent-daemon.js +0 -1774
- package/dist/client/chat-agent-daemon.js.map +0 -1
- package/dist/client/daemon-factory.d.ts +0 -106
- package/dist/client/daemon-factory.d.ts.map +0 -1
- package/dist/client/daemon-factory.js +0 -301
- package/dist/client/daemon-factory.js.map +0 -1
- package/dist/client/factory.d.ts +0 -111
- package/dist/client/factory.d.ts.map +0 -1
- package/dist/client/factory.js +0 -314
- package/dist/client/factory.js.map +0 -1
- package/dist/client/index.d.ts +0 -17
- package/dist/client/index.d.ts.map +0 -1
- package/dist/client/index.js +0 -38
- package/dist/client/index.js.map +0 -1
- package/dist/client/multi-bot-manager.d.ts +0 -42
- package/dist/client/multi-bot-manager.d.ts.map +0 -1
- package/dist/client/multi-bot-manager.js +0 -161
- package/dist/client/multi-bot-manager.js.map +0 -1
- package/dist/client/orchestrator-daemon.d.ts +0 -87
- package/dist/client/orchestrator-daemon.d.ts.map +0 -1
- package/dist/client/orchestrator-daemon.js +0 -444
- package/dist/client/orchestrator-daemon.js.map +0 -1
- package/dist/client/server.d.ts +0 -8
- package/dist/client/server.d.ts.map +0 -1
- package/dist/client/server.js +0 -251
- package/dist/client/server.js.map +0 -1
- package/dist/client/services/agent-registry.d.ts +0 -108
- package/dist/client/services/agent-registry.d.ts.map +0 -1
- package/dist/client/services/agent-registry.js +0 -630
- package/dist/client/services/agent-registry.js.map +0 -1
- package/dist/client/services/conversation-manager.d.ts +0 -50
- package/dist/client/services/conversation-manager.d.ts.map +0 -1
- package/dist/client/services/conversation-manager.js +0 -136
- package/dist/client/services/conversation-manager.js.map +0 -1
- package/dist/client/services/mcp-client.d.ts +0 -48
- package/dist/client/services/mcp-client.d.ts.map +0 -1
- package/dist/client/services/mcp-client.js +0 -105
- package/dist/client/services/mcp-client.js.map +0 -1
- package/dist/client/services/message-classifier.d.ts +0 -37
- package/dist/client/services/message-classifier.d.ts.map +0 -1
- package/dist/client/services/message-classifier.js +0 -187
- package/dist/client/services/message-classifier.js.map +0 -1
- package/dist/client/services/message-formatter.d.ts +0 -84
- package/dist/client/services/message-formatter.d.ts.map +0 -1
- package/dist/client/services/message-formatter.js +0 -353
- package/dist/client/services/message-formatter.js.map +0 -1
- package/dist/client/services/session-logger.d.ts +0 -106
- package/dist/client/services/session-logger.d.ts.map +0 -1
- package/dist/client/services/session-logger.js +0 -446
- package/dist/client/services/session-logger.js.map +0 -1
- package/dist/client/services/tool-executor.d.ts +0 -41
- package/dist/client/services/tool-executor.d.ts.map +0 -1
- package/dist/client/services/tool-executor.js +0 -169
- package/dist/client/services/tool-executor.js.map +0 -1
- package/dist/client/services/workspace-schema-cache.d.ts +0 -149
- package/dist/client/services/workspace-schema-cache.d.ts.map +0 -1
- package/dist/client/services/workspace-schema-cache.js +0 -732
- package/dist/client/services/workspace-schema-cache.js.map +0 -1
- package/dist/client/specialist-daemon.d.ts +0 -77
- package/dist/client/specialist-daemon.d.ts.map +0 -1
- package/dist/client/specialist-daemon.js +0 -197
- package/dist/client/specialist-daemon.js.map +0 -1
- package/dist/client/specialists.d.ts +0 -53
- package/dist/client/specialists.d.ts.map +0 -1
- package/dist/client/specialists.js +0 -178
- package/dist/client/specialists.js.map +0 -1
- package/dist/client/tool-schema-loader.d.ts +0 -62
- package/dist/client/tool-schema-loader.d.ts.map +0 -1
- package/dist/client/tool-schema-loader.js +0 -232
- package/dist/client/tool-schema-loader.js.map +0 -1
- package/dist/client/types.d.ts +0 -327
- package/dist/client/types.d.ts.map +0 -1
- package/dist/client/types.js +0 -121
- package/dist/client/types.js.map +0 -1
- package/dist/commands/seed-config.d.ts +0 -9
- package/dist/commands/seed-config.d.ts.map +0 -1
- package/dist/commands/seed-config.js +0 -377
- package/dist/commands/seed-config.js.map +0 -1
- package/dist/commands/setup.d.ts +0 -11
- package/dist/commands/setup.d.ts.map +0 -1
- package/dist/commands/setup.js +0 -320
- package/dist/commands/setup.js.map +0 -1
- package/dist/lib/discussion-lock.d.ts +0 -42
- package/dist/lib/discussion-lock.d.ts.map +0 -1
- package/dist/lib/discussion-lock.js +0 -110
- package/dist/lib/discussion-lock.js.map +0 -1
- package/dist/mcp/signal-handler.d.ts +0 -82
- package/dist/mcp/signal-handler.d.ts.map +0 -1
- package/dist/mcp/signal-handler.js +0 -406
- package/dist/mcp/signal-handler.js.map +0 -1
- package/dist/mcp/tools/__tests__/discussion-forward.test.d.ts +0 -2
- package/dist/mcp/tools/__tests__/discussion-forward.test.d.ts.map +0 -1
- package/dist/mcp/tools/__tests__/discussion-forward.test.js +0 -218
- package/dist/mcp/tools/__tests__/discussion-forward.test.js.map +0 -1
- package/dist/mcp/tools/app-member.d.ts +0 -14
- package/dist/mcp/tools/app-member.d.ts.map +0 -1
- package/dist/mcp/tools/app-member.js +0 -195
- package/dist/mcp/tools/app-member.js.map +0 -1
- package/dist/mcp/tools/app-scaffold.d.ts +0 -14
- package/dist/mcp/tools/app-scaffold.d.ts.map +0 -1
- package/dist/mcp/tools/app-scaffold.js +0 -581
- package/dist/mcp/tools/app-scaffold.js.map +0 -1
- package/dist/mcp/tools/bot-config/constants.d.ts +0 -23
- package/dist/mcp/tools/bot-config/constants.d.ts.map +0 -1
- package/dist/mcp/tools/bot-config/constants.js +0 -94
- package/dist/mcp/tools/bot-config/constants.js.map +0 -1
- package/dist/mcp/tools/bot-config/core.d.ts +0 -253
- package/dist/mcp/tools/bot-config/core.d.ts.map +0 -1
- package/dist/mcp/tools/bot-config/core.js +0 -2456
- package/dist/mcp/tools/bot-config/core.js.map +0 -1
- package/dist/mcp/tools/bot-config/index.d.ts +0 -10
- package/dist/mcp/tools/bot-config/index.d.ts.map +0 -1
- package/dist/mcp/tools/bot-config/index.js +0 -59
- package/dist/mcp/tools/bot-config/index.js.map +0 -1
- package/dist/mcp/tools/bot-config/tools.d.ts +0 -7
- package/dist/mcp/tools/bot-config/tools.d.ts.map +0 -1
- package/dist/mcp/tools/bot-config/tools.js +0 -15
- package/dist/mcp/tools/bot-config/tools.js.map +0 -1
- package/dist/mcp/tools/bot-config/types.d.ts +0 -50
- package/dist/mcp/tools/bot-config/types.d.ts.map +0 -1
- package/dist/mcp/tools/bot-config/types.js +0 -6
- package/dist/mcp/tools/bot-config/types.js.map +0 -1
- package/dist/mcp/tools/bug-fixer-tools.d.ts +0 -45
- package/dist/mcp/tools/bug-fixer-tools.d.ts.map +0 -1
- package/dist/mcp/tools/bug-fixer-tools.js +0 -1096
- package/dist/mcp/tools/bug-fixer-tools.js.map +0 -1
- package/dist/mcp/tools/document.d.ts +0 -11
- package/dist/mcp/tools/document.d.ts.map +0 -1
- package/dist/mcp/tools/document.js +0 -741
- package/dist/mcp/tools/document.js.map +0 -1
- package/dist/mcp/tools/investigate.d.ts +0 -9
- package/dist/mcp/tools/investigate.d.ts.map +0 -1
- package/dist/mcp/tools/investigate.js +0 -254
- package/dist/mcp/tools/investigate.js.map +0 -1
- package/dist/mcp/utils/pagination.d.ts +0 -40
- package/dist/mcp/utils/pagination.d.ts.map +0 -1
- package/dist/mcp/utils/pagination.js +0 -55
- package/dist/mcp/utils/pagination.js.map +0 -1
- package/dist/modules/bug-reports/bug-config.d.ts +0 -25
- package/dist/modules/bug-reports/bug-config.d.ts.map +0 -1
- package/dist/modules/bug-reports/bug-config.js +0 -187
- package/dist/modules/bug-reports/bug-config.js.map +0 -1
- package/dist/modules/bug-reports/bug-monitor.d.ts +0 -108
- package/dist/modules/bug-reports/bug-monitor.d.ts.map +0 -1
- package/dist/modules/bug-reports/bug-monitor.js +0 -510
- package/dist/modules/bug-reports/bug-monitor.js.map +0 -1
- package/dist/modules/bug-reports/giuseppe-agent.d.ts +0 -58
- package/dist/modules/bug-reports/giuseppe-agent.d.ts.map +0 -1
- package/dist/modules/bug-reports/giuseppe-agent.js +0 -467
- package/dist/modules/bug-reports/giuseppe-agent.js.map +0 -1
- package/dist/modules/bug-reports/giuseppe-ai.d.ts +0 -83
- package/dist/modules/bug-reports/giuseppe-ai.d.ts.map +0 -1
- package/dist/modules/bug-reports/giuseppe-ai.js +0 -466
- package/dist/modules/bug-reports/giuseppe-ai.js.map +0 -1
- package/dist/modules/bug-reports/giuseppe-bot.d.ts +0 -110
- package/dist/modules/bug-reports/giuseppe-bot.d.ts.map +0 -1
- package/dist/modules/bug-reports/giuseppe-bot.js +0 -804
- package/dist/modules/bug-reports/giuseppe-bot.js.map +0 -1
- package/dist/modules/bug-reports/giuseppe-daemon.d.ts +0 -80
- package/dist/modules/bug-reports/giuseppe-daemon.d.ts.map +0 -1
- package/dist/modules/bug-reports/giuseppe-daemon.js +0 -617
- package/dist/modules/bug-reports/giuseppe-daemon.js.map +0 -1
- package/dist/modules/bug-reports/giuseppe-files.d.ts +0 -64
- package/dist/modules/bug-reports/giuseppe-files.d.ts.map +0 -1
- package/dist/modules/bug-reports/giuseppe-files.js +0 -375
- package/dist/modules/bug-reports/giuseppe-files.js.map +0 -1
- package/dist/modules/bug-reports/giuseppe-git.d.ts +0 -48
- package/dist/modules/bug-reports/giuseppe-git.d.ts.map +0 -1
- package/dist/modules/bug-reports/giuseppe-git.js +0 -298
- package/dist/modules/bug-reports/giuseppe-git.js.map +0 -1
- package/dist/modules/bug-reports/giuseppe-lsp.d.ts +0 -113
- package/dist/modules/bug-reports/giuseppe-lsp.d.ts.map +0 -1
- package/dist/modules/bug-reports/giuseppe-lsp.js +0 -485
- package/dist/modules/bug-reports/giuseppe-lsp.js.map +0 -1
- package/dist/modules/bug-reports/giuseppe-prompt.d.ts +0 -5
- package/dist/modules/bug-reports/giuseppe-prompt.d.ts.map +0 -1
- package/dist/modules/bug-reports/giuseppe-prompt.js +0 -94
- package/dist/modules/bug-reports/giuseppe-prompt.js.map +0 -1
- package/dist/modules/bug-reports/index.d.ts +0 -77
- package/dist/modules/bug-reports/index.d.ts.map +0 -1
- package/dist/modules/bug-reports/index.js +0 -215
- package/dist/modules/bug-reports/index.js.map +0 -1
- package/dist/modules/bug-reports/pending-classification-registry.d.ts +0 -28
- package/dist/modules/bug-reports/pending-classification-registry.d.ts.map +0 -1
- package/dist/modules/bug-reports/pending-classification-registry.js +0 -50
- package/dist/modules/bug-reports/pending-classification-registry.js.map +0 -1
- package/dist/modules/bug-reports/pending-fix-registry.d.ts +0 -30
- package/dist/modules/bug-reports/pending-fix-registry.d.ts.map +0 -1
- package/dist/modules/bug-reports/pending-fix-registry.js +0 -42
- package/dist/modules/bug-reports/pending-fix-registry.js.map +0 -1
- package/dist/modules/bug-reports/pending-registry.d.ts +0 -27
- package/dist/modules/bug-reports/pending-registry.d.ts.map +0 -1
- package/dist/modules/bug-reports/pending-registry.js +0 -49
- package/dist/modules/bug-reports/pending-registry.js.map +0 -1
- package/dist/modules/bug-reports/types.d.ts +0 -123
- package/dist/modules/bug-reports/types.d.ts.map +0 -1
- package/dist/modules/bug-reports/types.js +0 -9
- package/dist/modules/bug-reports/types.js.map +0 -1
- package/dist/plugins/bug-fixer/index.d.ts +0 -2
- package/dist/plugins/bug-fixer/index.d.ts.map +0 -1
- package/dist/plugins/bug-fixer/index.js +0 -18
- package/dist/plugins/bug-fixer/index.js.map +0 -1
- package/dist/plugins/bug-fixer/tools.d.ts +0 -45
- package/dist/plugins/bug-fixer/tools.d.ts.map +0 -1
- package/dist/plugins/bug-fixer/tools.js +0 -1096
- package/dist/plugins/bug-fixer/tools.js.map +0 -1
- package/dist/plugins/vipunen/__tests__/tools.test.d.ts +0 -10
- package/dist/plugins/vipunen/__tests__/tools.test.d.ts.map +0 -1
- package/dist/plugins/vipunen/__tests__/tools.test.js +0 -646
- package/dist/plugins/vipunen/__tests__/tools.test.js.map +0 -1
- package/dist/routes/agents.d.ts +0 -44
- package/dist/routes/agents.d.ts.map +0 -1
- package/dist/routes/agents.js +0 -311
- package/dist/routes/agents.js.map +0 -1
- package/dist/services/agent-credential-store.d.ts +0 -73
- package/dist/services/agent-credential-store.d.ts.map +0 -1
- package/dist/services/agent-credential-store.js +0 -212
- package/dist/services/agent-credential-store.js.map +0 -1
- package/dist/stdio-server.d.ts +0 -14
- package/dist/stdio-server.d.ts.map +0 -1
- package/dist/stdio-server.js +0 -101
- package/dist/stdio-server.js.map +0 -1
- package/dist/workspace/context.d.ts +0 -148
- package/dist/workspace/context.d.ts.map +0 -1
- package/dist/workspace/context.js +0 -339
- package/dist/workspace/context.js.map +0 -1
- package/dist/workspace/credentials.d.ts +0 -55
- package/dist/workspace/credentials.d.ts.map +0 -1
- package/dist/workspace/credentials.js +0 -239
- package/dist/workspace/credentials.js.map +0 -1
- package/dist/workspace/index.d.ts +0 -21
- package/dist/workspace/index.d.ts.map +0 -1
- package/dist/workspace/index.js +0 -45
- package/dist/workspace/index.js.map +0 -1
- package/dist/workspace/loader.d.ts +0 -27
- package/dist/workspace/loader.d.ts.map +0 -1
- package/dist/workspace/loader.js +0 -222
- package/dist/workspace/loader.js.map +0 -1
- package/dist/workspace/schema.d.ts +0 -37
- package/dist/workspace/schema.d.ts.map +0 -1
- package/dist/workspace/schema.js +0 -192
- package/dist/workspace/schema.js.map +0 -1
|
@@ -39,11 +39,12 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
39
39
|
};
|
|
40
40
|
})();
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.clearAdminKeyErrors = clearAdminKeyErrors;
|
|
42
43
|
exports.generateWebhookSignature = generateWebhookSignature;
|
|
43
44
|
exports.verifyWebhookSignature = verifyWebhookSignature;
|
|
44
45
|
exports.getWebhookToken = getWebhookToken;
|
|
45
46
|
exports.getWebhookPath = getWebhookPath;
|
|
46
|
-
exports.
|
|
47
|
+
exports.destroyBot = destroyBot;
|
|
47
48
|
exports.handleBotConfigWebhook = handleBotConfigWebhook;
|
|
48
49
|
exports.listWorkspaceConfigs = listWorkspaceConfigs;
|
|
49
50
|
const fs = __importStar(require("fs"));
|
|
@@ -52,23 +53,608 @@ const crypto = __importStar(require("crypto"));
|
|
|
52
53
|
const logger_1 = require("../lib/logger");
|
|
53
54
|
const config_1 = require("../config");
|
|
54
55
|
const constants_1 = require("../bot-config/constants");
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
_loadConfigFile = botConfig.loadWorkspaceConfigFile;
|
|
64
|
-
_saveConfigFile = botConfig.saveWorkspaceConfigFile;
|
|
65
|
-
_listConfigFiles = botConfig.listWorkspaceConfigFiles;
|
|
66
|
-
}
|
|
67
|
-
catch {
|
|
68
|
-
// Not available in MCP-only installs — safe to skip
|
|
69
|
-
}
|
|
56
|
+
const loader_1 = require("../bot-config/loader");
|
|
57
|
+
const activity_error_1 = require("../bot-config/activity-error");
|
|
58
|
+
const events_1 = require("../bot-config/events");
|
|
59
|
+
const workspace_admin_store_1 = require("./workspace-admin-store");
|
|
60
|
+
const hailer_clients_1 = require("./hailer-clients");
|
|
61
|
+
const hailer_rpc_1 = require("./hailer-rpc");
|
|
62
|
+
const tool_helpers_1 = require("./utils/tool-helpers");
|
|
63
|
+
const hailer_api_client_1 = require("./utils/hailer-api-client");
|
|
70
64
|
const logger = (0, logger_1.createLogger)({ component: 'webhook-handler' });
|
|
71
65
|
const WEBHOOK_SECRET_FILE = 'webhook-secret.txt';
|
|
66
|
+
// AI Hub writes a "pending-…" placeholder into the password field at activity
|
|
67
|
+
// create time; we treat that as absent so we know to generate a real one.
|
|
68
|
+
const PENDING_PASSWORD_PREFIX = 'pending-';
|
|
69
|
+
const BOT_EMAIL_DOMAIN = 'hailer.com';
|
|
70
|
+
function generateSecurePassword() {
|
|
71
|
+
return crypto.randomBytes(16).toString('hex');
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Mirror of AI Hub's `generateBotEmail` (ai-hub/src/api.ts) — used by the
|
|
75
|
+
* collision-retry path when Hailer rejects the AI Hub-supplied email with a
|
|
76
|
+
* duplicate (code:409) AND no sibling activity provisioned the bot. We
|
|
77
|
+
* regenerate locally, write the new email back to the activity, and retry
|
|
78
|
+
* the register call.
|
|
79
|
+
*/
|
|
80
|
+
function generateBotEmail(firstname, lastname, workspaceId) {
|
|
81
|
+
const sanitize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
82
|
+
const first = sanitize(firstname) || 'bot';
|
|
83
|
+
const last = sanitize(lastname);
|
|
84
|
+
const wsPrefix = sanitize(workspaceId).slice(0, 8);
|
|
85
|
+
const suffix = crypto.randomBytes(3).toString('hex');
|
|
86
|
+
const parts = [last ? `${first}.${last}` : first];
|
|
87
|
+
if (wsPrefix) {
|
|
88
|
+
parts.push(wsPrefix);
|
|
89
|
+
}
|
|
90
|
+
parts.push(suffix);
|
|
91
|
+
return `${parts.join('-')}@${BOT_EMAIL_DOMAIN}`;
|
|
92
|
+
}
|
|
93
|
+
// Hailer fires multiple webhooks per phase change (one per phase listening),
|
|
94
|
+
// so siblings race to provision the same bot. Re-read the activity to detect
|
|
95
|
+
// a sibling that already wrote hailerProfile/password — and to detect that
|
|
96
|
+
// the activity has been deleted out from under us.
|
|
97
|
+
//
|
|
98
|
+
// activities.load returns `fields` as an object keyed by the field's logical
|
|
99
|
+
// key (e.g. `hailerProfile`, `password`) — same shape v3.activity.updateMany
|
|
100
|
+
// accepts. No workflow-definition lookup needed.
|
|
101
|
+
async function probeActivity(adminClient, activityId) {
|
|
102
|
+
try {
|
|
103
|
+
const activity = await adminClient.socket.request(hailer_rpc_1.HailerRpc.Activities.load, [activityId]);
|
|
104
|
+
const fields = activity?.fields ?? {};
|
|
105
|
+
const uid = fields.hailerProfile || undefined;
|
|
106
|
+
const activityEmail = fields.agentEmailInHailer || undefined;
|
|
107
|
+
const rawPassword = fields.password;
|
|
108
|
+
const password = rawPassword && !String(rawPassword).startsWith(PENDING_PASSWORD_PREFIX)
|
|
109
|
+
? String(rawPassword)
|
|
110
|
+
: undefined;
|
|
111
|
+
return { uid, password, email: activityEmail, discussion: activity?.discussion };
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
if ((0, activity_error_1.isActivityGone)(err)) {
|
|
115
|
+
logger.debug('[Webhook] probe: activity is deleted', { activityId });
|
|
116
|
+
return { gone: true };
|
|
117
|
+
}
|
|
118
|
+
logger.debug('[Webhook] activities.load (probe) failed', {
|
|
119
|
+
activityId,
|
|
120
|
+
error: (0, tool_helpers_1.extractErrorMessage)(err),
|
|
121
|
+
});
|
|
122
|
+
return {};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Hailer's concurrent-registration rejection codes:
|
|
126
|
+
// 409 "Email already registered!" — a sibling already created the user
|
|
127
|
+
// 127 "Unable to create new user." — race-guard
|
|
128
|
+
function isRegisterRaceError(err) {
|
|
129
|
+
const msg = (0, tool_helpers_1.extractErrorMessage)(err);
|
|
130
|
+
return msg.includes('"code":409') || msg.includes('"code":127');
|
|
131
|
+
}
|
|
132
|
+
function resolveUserDisplayName(users, uid) {
|
|
133
|
+
if (!uid || !users) {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
const u = users[uid];
|
|
137
|
+
if (!u) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
const name = [u.firstname, u.lastname].filter(Boolean).join(' ').trim();
|
|
141
|
+
return name || u.email || undefined;
|
|
142
|
+
}
|
|
143
|
+
// Per-activity provisioning mutex. Hailer fires multiple webhooks per activity
|
|
144
|
+
// create (initial create + phase transition + …) and they hit Express in
|
|
145
|
+
// parallel. Without serialization each one independently calls v2.user.register
|
|
146
|
+
// before any sibling has written its uid back to the activity, so probeActivity
|
|
147
|
+
// keeps missing the in-flight provision — net result is N orphan Hailer users
|
|
148
|
+
// per bot and a final activity whose password no longer matches Hailer's record.
|
|
149
|
+
const provisionLocks = new Map();
|
|
150
|
+
// Track the last admin-key value we successfully persisted per workspace so we
|
|
151
|
+
// skip the Hailer-login validation roundtrip on every subsequent webhook that
|
|
152
|
+
// carries the same key. SHA-256 of the raw value — never store the plaintext.
|
|
153
|
+
// Cleared on process restart, which is fine: the first webhook re-validates and
|
|
154
|
+
// re-caches.
|
|
155
|
+
const lastAdminKeyHashByWorkspace = new Map();
|
|
156
|
+
/**
|
|
157
|
+
* Decode an admin API key stored in schemaConfig. AI Hub writes the key as
|
|
158
|
+
* `hex:<hex-encoded-utf8>` so a casual reader of the workspace data doesn't
|
|
159
|
+
* see the plaintext credential. Values without the `hex:` prefix are
|
|
160
|
+
* treated as legacy plaintext for backward compatibility with carriers
|
|
161
|
+
* written before this change shipped.
|
|
162
|
+
*/
|
|
163
|
+
function decodeAdminKey(raw) {
|
|
164
|
+
if (!raw.startsWith('hex:')) {
|
|
165
|
+
return raw;
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
return Buffer.from(raw.slice(4), 'hex').toString('utf8');
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
return raw;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Surface an admin-key rejection reason back to AI Hub through the activity's
|
|
176
|
+
* `schemaConfig.adminKeyError` — the only channel AI Hub can read without
|
|
177
|
+
* reaching the (browser-blocked, HTTP) MCP server directly. Writes only on a
|
|
178
|
+
* state change, so a re-delivered webhook can't loop. Best-effort: a failure
|
|
179
|
+
* here must never break the webhook flow.
|
|
180
|
+
*/
|
|
181
|
+
async function markAdminKeyError(client, activityId, schemaConfig, error) {
|
|
182
|
+
const current = typeof schemaConfig.adminKeyError === 'string' ? schemaConfig.adminKeyError : null;
|
|
183
|
+
if (current === error) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const next = { ...schemaConfig };
|
|
187
|
+
if (error) {
|
|
188
|
+
next.adminKeyError = error;
|
|
189
|
+
}
|
|
190
|
+
if (!error) {
|
|
191
|
+
delete next.adminKeyError;
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
await client.socket.request(hailer_rpc_1.HailerRpc.Activities.updateMany, [[
|
|
195
|
+
{ _id: activityId, fields: { schemaConfig: JSON.stringify(next) } },
|
|
196
|
+
]]);
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
logger.warn('[Webhook] Failed to write adminKeyError status to activity', {
|
|
200
|
+
activityId, error: (0, tool_helpers_1.extractErrorMessage)(err),
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// v2.core.init returns processes as an object keyed by process id on the
|
|
205
|
+
// live API, but parts of this codebase have also seen the array form (see
|
|
206
|
+
// normalizeInitProcesses in mcp/utils/types.ts and the array iteration in
|
|
207
|
+
// bot-config/workflow-installer.ts). Accept both so a shape drift can't
|
|
208
|
+
// silently turn the sweep into a no-op.
|
|
209
|
+
function toProcessMap(raw) {
|
|
210
|
+
if (!Array.isArray(raw)) {
|
|
211
|
+
return (raw ?? {});
|
|
212
|
+
}
|
|
213
|
+
const withIds = raw.filter((proc) => Boolean(proc?._id));
|
|
214
|
+
const map = {};
|
|
215
|
+
for (const proc of withIds) {
|
|
216
|
+
map[proc._id] = proc;
|
|
217
|
+
}
|
|
218
|
+
return map;
|
|
219
|
+
}
|
|
220
|
+
// Identify the Agent Directory structurally (field keys written by AI Hub's
|
|
221
|
+
// installer) rather than by name — names are localized ('🤖 Agent Directory'
|
|
222
|
+
// / '🤖 Agentit') and user-editable.
|
|
223
|
+
function looksLikeAgentDirectory(wf) {
|
|
224
|
+
const fieldKeys = new Set(Object.values(wf?.fields ?? {}).map((field) => field?.key));
|
|
225
|
+
return fieldKeys.has('schemaConfig') && fieldKeys.has('agentEmailInHailer');
|
|
226
|
+
}
|
|
227
|
+
async function clearAdminKeyErrorOnActivity(client, activityId) {
|
|
228
|
+
try {
|
|
229
|
+
const activity = await client.socket.request(hailer_rpc_1.HailerRpc.Activities.load, [activityId]);
|
|
230
|
+
const raw = activity?.fields?.schemaConfig;
|
|
231
|
+
if (typeof raw !== 'string' || !raw) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const parsed = JSON.parse(raw);
|
|
235
|
+
if (typeof parsed.adminKeyError !== 'string') {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
delete parsed.adminKeyError;
|
|
239
|
+
await client.socket.request(hailer_rpc_1.HailerRpc.Activities.updateMany, [[
|
|
240
|
+
{ _id: activityId, fields: { schemaConfig: JSON.stringify(parsed) } },
|
|
241
|
+
]]);
|
|
242
|
+
logger.info('[AdminKey] Cleared stale adminKeyError marker', { activityId });
|
|
243
|
+
}
|
|
244
|
+
catch (err) {
|
|
245
|
+
logger.debug('[AdminKey] clearAdminKeyErrors: skipped activity (non-fatal)', {
|
|
246
|
+
activityId, error: (0, tool_helpers_1.extractErrorMessage)(err),
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
async function clearAdminKeyErrorsInPhase(client, workflowId, phaseId) {
|
|
251
|
+
const result = await client.socket.request(hailer_rpc_1.HailerRpc.Activities.list, [
|
|
252
|
+
{ processId: workflowId, phaseId },
|
|
253
|
+
{ limit: 100, returnFlat: true },
|
|
254
|
+
]);
|
|
255
|
+
const entries = result?.activities || result?.list || result?.data || [];
|
|
256
|
+
for (const entry of entries) {
|
|
257
|
+
if (!entry?._id) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
await clearAdminKeyErrorOnActivity(client, entry._id);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Sweep ALL activities of the workspace's Agent Directory and remove stale
|
|
265
|
+
* `schemaConfig.adminKeyError` markers. markAdminKeyError only clears the
|
|
266
|
+
* single activity whose webhook delivered a verified key, and the popup
|
|
267
|
+
* /admin-authorize path bypasses webhooks entirely — either way a marker
|
|
268
|
+
* left on a sibling (carrier or other bot) wedges AI Hub's red 'Verify
|
|
269
|
+
* email' pill: readAdminKeyErrorFromWorkflow scans every activity and any
|
|
270
|
+
* error wins over connected. Called from both success paths once a verified
|
|
271
|
+
* admin key is persisted. Best-effort by contract: never throws.
|
|
272
|
+
*/
|
|
273
|
+
async function clearAdminKeyErrors(client, workspaceId, workflowId) {
|
|
274
|
+
try {
|
|
275
|
+
await (0, hailer_clients_1.withApiKeyLock)(client.sessionKey, async () => {
|
|
276
|
+
// Pin the key's ambient workspace pointer — it follows whatever
|
|
277
|
+
// the admin last viewed (same reason provisionBotUserLocked pins).
|
|
278
|
+
await client.socket.request(hailer_rpc_1.HailerRpc.Core.switchEcosystem, [workspaceId]);
|
|
279
|
+
const init = await client.socket.request(hailer_rpc_1.HailerRpc.Core.init, [['processes']]);
|
|
280
|
+
const processes = toProcessMap(init?.processes);
|
|
281
|
+
// init [['processes']] returns workflows from EVERY workspace the
|
|
282
|
+
// key can reach (see findExistingWorkflow in workflow-installer.ts)
|
|
283
|
+
// — without the cid filter a multi-workspace admin could resolve a
|
|
284
|
+
// sibling workspace's directory and leave the real markers wedged.
|
|
285
|
+
const resolvedId = workflowId ?? Object.keys(processes).find((id) => processes[id]?.cid === workspaceId && looksLikeAgentDirectory(processes[id]));
|
|
286
|
+
const phaseIds = resolvedId ? Object.keys(processes[resolvedId]?.phases ?? {}) : [];
|
|
287
|
+
if (!resolvedId || phaseIds.length === 0) {
|
|
288
|
+
logger.debug('[AdminKey] clearAdminKeyErrors: agent workflow not resolvable — nothing to clear', {
|
|
289
|
+
workspaceId, workflowId,
|
|
290
|
+
});
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
for (const phaseId of phaseIds) {
|
|
294
|
+
await clearAdminKeyErrorsInPhase(client, resolvedId, phaseId);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
catch (err) {
|
|
299
|
+
logger.warn('[AdminKey] clearAdminKeyErrors failed (non-fatal)', {
|
|
300
|
+
workspaceId, error: (0, tool_helpers_1.extractErrorMessage)(err),
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* If a webhook operation failed with an auth-shaped error ('Invalid
|
|
306
|
+
* Session', Unauthorized, "code":401/403), decide whether the ADMIN KEY
|
|
307
|
+
* itself is dead before forgetting it. Hailer uses code 403 for every
|
|
308
|
+
* permission denial — invite.send 'Validate your email.', a per-activity
|
|
309
|
+
* updateMany rejection — and none of those say anything about the key.
|
|
310
|
+
* So re-probe the key directly: getValidatedAdminRecord runs core.init
|
|
311
|
+
* with the stored key alone and removes the record only when THAT call
|
|
312
|
+
* is rejected (transient errors keep it). On removal, also clear the
|
|
313
|
+
* dedupe hash so a re-pasted key re-validates. Subsequent webhooks then
|
|
314
|
+
* skip cleanly ('No admin credentials') instead of hammering 403.
|
|
315
|
+
* Returns true if the credentials were cleared.
|
|
316
|
+
*/
|
|
317
|
+
async function forgetAdminKeyOnAuthFailure(workspaceId, err) {
|
|
318
|
+
if (!(0, workspace_admin_store_1.looksLikeAuthFailure)((0, tool_helpers_1.extractErrorMessage)(err))) {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
const record = await (0, workspace_admin_store_1.getValidatedAdminRecord)(workspaceId);
|
|
322
|
+
if (record) {
|
|
323
|
+
logger.warn('[Webhook] Operation failed with 401/403 but the stored admin key still validates — keeping credentials', {
|
|
324
|
+
workspaceId, error: (0, tool_helpers_1.extractErrorMessage)(err),
|
|
325
|
+
});
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
lastAdminKeyHashByWorkspace.delete(workspaceId);
|
|
329
|
+
logger.warn('[Webhook] Admin key rejected (auth failure) — cleared stored credentials; webhooks will skip until a valid key is re-connected', { workspaceId });
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* If the webhook payload's schemaConfig carries `adminApiKey`, validate it and
|
|
334
|
+
* persist as the workspace's admin credentials. This is the workflow-field
|
|
335
|
+
* delivery path: AI Hub writes the admin's user-api-key into schemaConfig and
|
|
336
|
+
* Hailer's phase webhook brings it server-side here, sidestepping the
|
|
337
|
+
* mixed-content blocker on the old browser-mediated /admin-authorize flow.
|
|
338
|
+
*
|
|
339
|
+
* Safe to call on every webhook — same-value writes are short-circuited by
|
|
340
|
+
* the hash cache, so we don't hammer Hailer with a v2.core.init per phase
|
|
341
|
+
* toggle.
|
|
342
|
+
*/
|
|
343
|
+
async function maybePersistAdminKeyFromSchema(workspaceId, schemaConfig, activityId, workflowId) {
|
|
344
|
+
const stored = schemaConfig?.adminApiKey;
|
|
345
|
+
if (!schemaConfig || typeof stored !== 'string' || !stored) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
const apiKey = decodeAdminKey(stored);
|
|
349
|
+
const hash = crypto.createHash('sha256').update(apiKey).digest('hex');
|
|
350
|
+
if (lastAdminKeyHashByWorkspace.get(workspaceId) === hash) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
try {
|
|
354
|
+
const client = await (0, hailer_clients_1.createHailerClientByApiKey)(apiKey);
|
|
355
|
+
const identity = await (0, workspace_admin_store_1.resolveAdminIdentity)(client);
|
|
356
|
+
if (!identity.uid) {
|
|
357
|
+
logger.warn('[Webhook] schemaConfig.adminApiKey: identity resolution returned no uid — not persisting', { workspaceId });
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
if (!identity.emailVerified) {
|
|
361
|
+
// Unverified admin email: Hailer's invite.send rejects the bot invite
|
|
362
|
+
// (403 'Validate your email.'), the activity never gets its
|
|
363
|
+
// hailerProfile stamp, and every webhook redelivery re-fires the
|
|
364
|
+
// invite — the reinvite doom loop. Skip persist, and deliberately do
|
|
365
|
+
// NOT set lastAdminKeyHashByWorkspace, so a re-check fires once the
|
|
366
|
+
// admin verifies. Fail-closed: missing/false counts as unverified.
|
|
367
|
+
logger.warn('[Webhook] schemaConfig.adminApiKey: admin email not verified — not persisting', { workspaceId, adminUid: identity.uid });
|
|
368
|
+
await markAdminKeyError(client, activityId, schemaConfig, 'email-not-verified');
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
(0, workspace_admin_store_1.persistAdminCredentials)(workspaceId, apiKey, identity);
|
|
372
|
+
lastAdminKeyHashByWorkspace.set(workspaceId, hash);
|
|
373
|
+
// Clear rejection markers on ALL Agent Directory activities (not just
|
|
374
|
+
// this one) now that a verified key is stored — a marker left on a
|
|
375
|
+
// sibling carrier/bot would wedge AI Hub's 'Verify email' pill.
|
|
376
|
+
await clearAdminKeyErrors(client, workspaceId, workflowId);
|
|
377
|
+
logger.info('[Webhook] Admin credentials updated from schemaConfig.adminApiKey', { workspaceId, adminUid: identity.uid });
|
|
378
|
+
}
|
|
379
|
+
catch (err) {
|
|
380
|
+
logger.warn('[Webhook] schemaConfig.adminApiKey: validation failed (key may be invalid or revoked)', {
|
|
381
|
+
workspaceId,
|
|
382
|
+
error: (0, tool_helpers_1.extractErrorMessage)(err),
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
// In-memory cache of the freshly-provisioned user, keyed by activityId. The
|
|
387
|
+
// lock alone is not enough: even after the leader's `activities.updateMany`
|
|
388
|
+
// resolves, Hailer's `activities.load` (used by probeActivity) can return a
|
|
389
|
+
// stale snapshot for a brief window, so a freshly-released sibling re-enters
|
|
390
|
+
// the register loop and orphans another user. Caching the leader's result
|
|
391
|
+
// in memory lets the sibling skip Hailer entirely. TTL is generous because
|
|
392
|
+
// the burst of sibling webhooks all fires within seconds of activity create.
|
|
393
|
+
const PROVISIONED_RESULT_TTL_MS = 5 * 60 * 1000;
|
|
394
|
+
const provisionedResults = new Map();
|
|
395
|
+
async function withProvisionLock(activityId, fn) {
|
|
396
|
+
const prev = provisionLocks.get(activityId) ?? Promise.resolve();
|
|
397
|
+
const next = prev.then(fn, fn);
|
|
398
|
+
provisionLocks.set(activityId, next);
|
|
399
|
+
try {
|
|
400
|
+
return await next;
|
|
401
|
+
}
|
|
402
|
+
finally {
|
|
403
|
+
if (provisionLocks.get(activityId) === next) {
|
|
404
|
+
provisionLocks.delete(activityId);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
async function provisionBotUser(adminClient, payload, workspaceId, email) {
|
|
409
|
+
// No inner withProvisionLock — handleBotConfigWebhook already holds the
|
|
410
|
+
// per-activity lock for the entire flow. Re-entering here would deadlock
|
|
411
|
+
// (same activityId waits on the outer's still-running promise). The
|
|
412
|
+
// provisionedResults cache still serves its purpose: a later webhook for
|
|
413
|
+
// the same activity within TTL skips the register loop entirely.
|
|
414
|
+
const cached = provisionedResults.get(payload._id);
|
|
415
|
+
if (cached) {
|
|
416
|
+
logger.info('[Webhook] Activity provisioned this session — reusing cached result', {
|
|
417
|
+
activityId: payload._id, uid: cached.uid, workspaceId,
|
|
418
|
+
});
|
|
419
|
+
return { ...cached, freshlyProvisioned: false };
|
|
420
|
+
}
|
|
421
|
+
const result = await provisionBotUserInner(adminClient, payload, workspaceId, email);
|
|
422
|
+
if (result?.uid && result.password) {
|
|
423
|
+
provisionedResults.set(payload._id, result);
|
|
424
|
+
const timer = setTimeout(() => {
|
|
425
|
+
provisionedResults.delete(payload._id);
|
|
426
|
+
}, PROVISIONED_RESULT_TTL_MS);
|
|
427
|
+
timer.unref?.();
|
|
428
|
+
}
|
|
429
|
+
return result;
|
|
430
|
+
}
|
|
431
|
+
async function provisionBotUserInner(adminClient, payload, workspaceId, email) {
|
|
432
|
+
// Wrap the entire provision flow in a per-API-key lock. The admin key's
|
|
433
|
+
// server-side "current workspace" is mutated by switchEcosystem and
|
|
434
|
+
// implicitly read by every subsequent call (init, probe, register).
|
|
435
|
+
// Without this lock, two workspaces sharing the same admin user would
|
|
436
|
+
// race the workspace context and provision into the wrong workspace.
|
|
437
|
+
return (0, hailer_clients_1.withApiKeyLock)(adminClient.sessionKey, () => provisionBotUserLocked(adminClient, payload, workspaceId, email));
|
|
438
|
+
}
|
|
439
|
+
async function provisionBotUserLocked(adminClient, payload, workspaceId, email) {
|
|
440
|
+
// Pin the admin's API-key context to the webhook's workspace. The key's
|
|
441
|
+
// current workspace follows whatever the admin last viewed in their
|
|
442
|
+
// browser, so we can't assume it matches `payload.cid`.
|
|
443
|
+
await adminClient.socket.request(hailer_rpc_1.HailerRpc.Core.switchEcosystem, [workspaceId]);
|
|
444
|
+
const sibling = await probeActivity(adminClient, payload._id);
|
|
445
|
+
if (sibling.gone) {
|
|
446
|
+
await removeBotFromConfig(workspaceId, payload._id);
|
|
447
|
+
logger.info('[Webhook] Activity no longer exists — skipping provision', {
|
|
448
|
+
activityId: payload._id, workspaceId,
|
|
449
|
+
});
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
if (sibling.uid) {
|
|
453
|
+
logger.info('[Webhook] Activity already provisioned — skipping register', {
|
|
454
|
+
activityId: payload._id, uid: sibling.uid, workspaceId,
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
if (sibling.uid && !sibling.password) {
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
if (sibling.uid && sibling.password) {
|
|
461
|
+
// The stamp proves the bot USER exists, not that the invite landed —
|
|
462
|
+
// hailerProfile/password are written BEFORE inviteSend (doom-loop
|
|
463
|
+
// fix below), so a transiently failed invite leaves a user that is
|
|
464
|
+
// not a workspace member. Verify membership and re-fire the invite
|
|
465
|
+
// before handing the credentials back for the config upsert.
|
|
466
|
+
await ensureWorkspaceMembership(adminClient, payload, workspaceId, sibling.email || email, sibling.uid);
|
|
467
|
+
return { uid: sibling.uid, password: sibling.password, freshlyProvisioned: false, discussion: sibling.discussion };
|
|
468
|
+
}
|
|
469
|
+
// teams → invite target; users → audit-message display name attribution.
|
|
470
|
+
const init = await adminClient.socket.request(hailer_rpc_1.HailerRpc.Core.init, [
|
|
471
|
+
['teams', 'users'],
|
|
472
|
+
]);
|
|
473
|
+
const newPassword = generateSecurePassword();
|
|
474
|
+
const firstname = getFieldValue(payload.fields, 'firstName') || 'Bot';
|
|
475
|
+
const lastname = getFieldValue(payload.fields, 'lastName') || payload.name;
|
|
476
|
+
const teamIds = init.teams ? Object.keys(init.teams) : [];
|
|
477
|
+
if (teamIds.length === 0) {
|
|
478
|
+
throw new Error('Workspace has no teams — cannot send invite');
|
|
479
|
+
}
|
|
480
|
+
const triggerUserName = resolveUserDisplayName(init.users, payload.uid);
|
|
481
|
+
// Register with retry-on-collision. 409 ("Email already registered") may be:
|
|
482
|
+
// (a) sibling activity won the race — recheck shows their uid, use it
|
|
483
|
+
// (b) email genuinely taken by an unrelated user (cross-workspace clash,
|
|
484
|
+
// birthday-paradox collision in random suffix) — regenerate and retry
|
|
485
|
+
// Bounded so we never spin against a permanently-stuck name.
|
|
486
|
+
const MAX_REGISTER_RETRIES = 3;
|
|
487
|
+
let uid = null;
|
|
488
|
+
for (let attempt = 0; attempt <= MAX_REGISTER_RETRIES; attempt++) {
|
|
489
|
+
try {
|
|
490
|
+
const result = await adminClient.socket.request(hailer_rpc_1.HailerRpc.User.register, [
|
|
491
|
+
{ email, firstname, lastname, password: newPassword },
|
|
492
|
+
{},
|
|
493
|
+
'en',
|
|
494
|
+
]);
|
|
495
|
+
uid = result.uid;
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
catch (registerErr) {
|
|
499
|
+
if (!isRegisterRaceError(registerErr)) {
|
|
500
|
+
throw registerErr;
|
|
501
|
+
}
|
|
502
|
+
// Sibling-race recheck only matters on the first 409. After that no
|
|
503
|
+
// sibling can appear (we hold the only provisioning attempt for this
|
|
504
|
+
// activity), so subsequent 409s are pure email collisions.
|
|
505
|
+
if (attempt === 0) {
|
|
506
|
+
const recheck = await probeActivity(adminClient, payload._id);
|
|
507
|
+
if (recheck.gone) {
|
|
508
|
+
logger.info('[Webhook] register hit race but activity is now gone — skipping', {
|
|
509
|
+
activityId: payload._id, workspaceId,
|
|
510
|
+
});
|
|
511
|
+
return null;
|
|
512
|
+
}
|
|
513
|
+
if (recheck.uid) {
|
|
514
|
+
logger.info('[Webhook] register lost race — using sibling-provisioned user', {
|
|
515
|
+
activityId: payload._id, uid: recheck.uid, workspaceId,
|
|
516
|
+
});
|
|
517
|
+
return recheck.password
|
|
518
|
+
? { uid: recheck.uid, password: recheck.password, freshlyProvisioned: false, discussion: recheck.discussion }
|
|
519
|
+
: null;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
if (attempt >= MAX_REGISTER_RETRIES) {
|
|
523
|
+
logger.warn('[Webhook] register: email collision exhausted retries — giving up', {
|
|
524
|
+
activityId: payload._id, lastEmail: (0, config_1.maskEmail)(email),
|
|
525
|
+
error: (0, tool_helpers_1.extractErrorMessage)(registerErr),
|
|
526
|
+
});
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
const nextEmail = generateBotEmail(firstname, lastname, workspaceId);
|
|
530
|
+
logger.info('[Webhook] register: email already registered — retrying with regenerated email', {
|
|
531
|
+
activityId: payload._id, attempt: attempt + 1,
|
|
532
|
+
previous: (0, config_1.maskEmail)(email), next: (0, config_1.maskEmail)(nextEmail),
|
|
533
|
+
});
|
|
534
|
+
// Write the new email back to the activity *before* the next register
|
|
535
|
+
// attempt — if a later step crashes after register succeeds, the
|
|
536
|
+
// activity must already show the email the user was registered with,
|
|
537
|
+
// not the original (now-taken) one.
|
|
538
|
+
await adminClient.socket.request(hailer_rpc_1.HailerRpc.Activities.updateMany, [[
|
|
539
|
+
{ _id: payload._id, fields: { agentEmailInHailer: nextEmail } },
|
|
540
|
+
]]);
|
|
541
|
+
email = nextEmail;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
if (!uid) {
|
|
545
|
+
// Defensive: the loop above either breaks with a uid set, returns, or
|
|
546
|
+
// exhausts retries (which returns null). Ending here means the control
|
|
547
|
+
// flow regressed — surface loudly rather than write empty strings to
|
|
548
|
+
// hailerProfile / use an unauthenticated invite.
|
|
549
|
+
throw new Error(`provisionBotUser: register loop fell through without a uid (activityId=${payload._id})`);
|
|
550
|
+
}
|
|
551
|
+
// Hailer member role on the workspace. Defaults to 'user' to match the
|
|
552
|
+
// AI Hub bot pattern; public-chat graduation hints 'admin' via the
|
|
553
|
+
// schemaConfig field so the freshly-graduated workspace's bot has the
|
|
554
|
+
// permission to install workflows, invite users, and otherwise act as
|
|
555
|
+
// the workspace's first administrator on the visitor's behalf.
|
|
556
|
+
const memberRole = readMemberRoleFromSchemaConfig(payload.fields);
|
|
557
|
+
// Stamp the activity with the freshly-registered uid BEFORE sending the
|
|
558
|
+
// invite. inviteSend can fail (e.g. Hailer returns 403 'Validate your
|
|
559
|
+
// email.' for an unverified admin); if the stamp came after, the activity
|
|
560
|
+
// would stay unprovisioned and every webhook redelivery would re-register a
|
|
561
|
+
// NEW bot user (email collision → regenerated email) and re-fire the invite
|
|
562
|
+
// — the reinvite doom loop. Stamping first means the next probeActivity
|
|
563
|
+
// short-circuits on sibling.uid, so a failed invite costs one attempt, not
|
|
564
|
+
// a storm (and never spawns duplicate bot users).
|
|
565
|
+
await adminClient.socket.request(hailer_rpc_1.HailerRpc.Activities.updateMany, [[
|
|
566
|
+
{ _id: payload._id, fields: { hailerProfile: uid, password: newPassword } },
|
|
567
|
+
]]);
|
|
568
|
+
await adminClient.socket.request(hailer_rpc_1.HailerRpc.Network.inviteSend, [
|
|
569
|
+
{ email, teams: [teamIds[0]], role: memberRole, name: `${firstname} ${lastname}`, cid: workspaceId },
|
|
570
|
+
]);
|
|
571
|
+
logger.info('[Webhook] Auto-created bot user', { email: (0, config_1.maskEmail)(email), uid, workspaceId });
|
|
572
|
+
return { uid, password: newPassword, freshlyProvisioned: true, discussion: sibling.discussion, triggerUserName };
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Recovery for the stamp-before-invite orphan: provisionBotUserLocked
|
|
576
|
+
* deliberately stamps hailerProfile/password before inviteSend, so a
|
|
577
|
+
* transient invite failure leaves a Hailer user that never became a
|
|
578
|
+
* workspace member while every later webhook short-circuits on
|
|
579
|
+
* sibling.uid. Detect the orphan (bot uid missing from
|
|
580
|
+
* init.network.members) and retry the invite. invite.send is safe to
|
|
581
|
+
* repeat: an existing member returns success without side effects, and a
|
|
582
|
+
* fresh bot user (member of no workspace) is added directly without a
|
|
583
|
+
* pending-invite record. Throws on failure so the caller's catch skips
|
|
584
|
+
* the config upsert — never start a bot that can't reach its workspace.
|
|
585
|
+
* Must run after switchEcosystem has pinned the admin context to
|
|
586
|
+
* workspaceId (the caller holds the API-key lock).
|
|
587
|
+
*/
|
|
588
|
+
async function ensureWorkspaceMembership(adminClient, payload, workspaceId, email, uid) {
|
|
589
|
+
const init = await adminClient.socket.request(hailer_rpc_1.HailerRpc.Core.init, [
|
|
590
|
+
['network', 'teams'],
|
|
591
|
+
]);
|
|
592
|
+
const isMember = (init.network?.members || []).some((member) => member.uid === uid);
|
|
593
|
+
if (isMember) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
const teamIds = init.teams ? Object.keys(init.teams) : [];
|
|
597
|
+
if (teamIds.length === 0) {
|
|
598
|
+
throw new Error('Workspace has no teams — cannot retry bot invite');
|
|
599
|
+
}
|
|
600
|
+
const firstname = getFieldValue(payload.fields, 'firstName') || 'Bot';
|
|
601
|
+
const lastname = getFieldValue(payload.fields, 'lastName') || payload.name;
|
|
602
|
+
const memberRole = readMemberRoleFromSchemaConfig(payload.fields);
|
|
603
|
+
logger.warn('[Webhook] Provisioned bot user is not a workspace member (earlier invite failed) — retrying invite', {
|
|
604
|
+
activityId: payload._id, uid, workspaceId, email: (0, config_1.maskEmail)(email),
|
|
605
|
+
});
|
|
606
|
+
await adminClient.socket.request(hailer_rpc_1.HailerRpc.Network.inviteSend, [
|
|
607
|
+
{ email, teams: [teamIds[0]], role: memberRole, name: `${firstname} ${lastname}`, cid: workspaceId },
|
|
608
|
+
]);
|
|
609
|
+
logger.info('[Webhook] Invite retry succeeded — bot user joined workspace', {
|
|
610
|
+
activityId: payload._id, uid, workspaceId,
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
// Best-effort audit trail in the bot's activity discussion. Failures must
|
|
614
|
+
// not surface — auto-provisioning has already succeeded by the time we get here.
|
|
615
|
+
//
|
|
616
|
+
// Held inside withApiKeyLock + a fresh switchEcosystem because provisionBotUser
|
|
617
|
+
// released the lock before returning. Without re-pinning the admin's server-
|
|
618
|
+
// side workspace pointer, a concurrent webhook for a different workspace
|
|
619
|
+
// could repoint the context and make activities.load resolve in the wrong
|
|
620
|
+
// workspace (or fail), even though messenger.send is discussion-scoped.
|
|
621
|
+
async function postProvisioningAudit(adminClient, workspaceId, activityId, result) {
|
|
622
|
+
await (0, hailer_clients_1.withApiKeyLock)(adminClient.sessionKey, async () => {
|
|
623
|
+
try {
|
|
624
|
+
await adminClient.socket.request(hailer_rpc_1.HailerRpc.Core.switchEcosystem, [workspaceId]);
|
|
625
|
+
let discussionId = result.discussion;
|
|
626
|
+
if (!discussionId) {
|
|
627
|
+
const activity = await adminClient.socket.request(hailer_rpc_1.HailerRpc.Activities.load, [activityId]);
|
|
628
|
+
discussionId = activity?.discussion;
|
|
629
|
+
}
|
|
630
|
+
if (!discussionId) {
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
// activities.follow is a TOGGLE — calling it raw on an admin who's
|
|
634
|
+
// already following (e.g. they created the activity in AI Hub) flips
|
|
635
|
+
// them OFF and they get kicked out of the discussion. Use the toggle-
|
|
636
|
+
// aware wrapper so the admin ends up following regardless of prior state.
|
|
637
|
+
try {
|
|
638
|
+
await new hailer_api_client_1.HailerApiClient(adminClient).joinActivityDiscussion(activityId);
|
|
639
|
+
}
|
|
640
|
+
catch (joinErr) {
|
|
641
|
+
logger.debug('[Webhook] joinActivityDiscussion on audit join failed (non-fatal)', {
|
|
642
|
+
activityId, error: (0, tool_helpers_1.extractErrorMessage)(joinErr),
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
const msg = result.triggerUserName
|
|
646
|
+
? `User auto-provisioned by ${result.triggerUserName}.`
|
|
647
|
+
: `User auto-provisioned.`;
|
|
648
|
+
await adminClient.socket.request(hailer_rpc_1.HailerRpc.Messenger.send, [{ msg }, discussionId]);
|
|
649
|
+
logger.debug('[Webhook] Posted audit discussion message', { activityId, discussionId });
|
|
650
|
+
}
|
|
651
|
+
catch (auditErr) {
|
|
652
|
+
logger.warn('[Webhook] Failed to post audit discussion message (non-fatal)', {
|
|
653
|
+
activityId, error: (0, tool_helpers_1.extractErrorMessage)(auditErr),
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
});
|
|
657
|
+
}
|
|
72
658
|
// ============================================================================
|
|
73
659
|
// WEBHOOK TOKEN SECURITY
|
|
74
660
|
// ============================================================================
|
|
@@ -161,71 +747,107 @@ function getWebhookPath() {
|
|
|
161
747
|
const token = getWebhookToken();
|
|
162
748
|
return token ? `/${token}` : null;
|
|
163
749
|
}
|
|
164
|
-
let botUpdateCallback = null;
|
|
165
|
-
function onBotUpdate(callback) {
|
|
166
|
-
botUpdateCallback = callback;
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Get field value from webhook payload by key
|
|
170
|
-
*/
|
|
171
750
|
function getFieldValue(fields, key) {
|
|
172
751
|
const field = fields.find((f) => f.key === key);
|
|
173
752
|
return field?.value ?? null;
|
|
174
753
|
}
|
|
175
754
|
/**
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
755
|
+
* Pull the optional `memberRole` hint out of the activity's schemaConfig
|
|
756
|
+
* JSON. Used by provisionBotUserInner to decide what Hailer member role
|
|
757
|
+
* to invite the bot account with: 'user' (default, matches AI Hub) or
|
|
758
|
+
* 'admin' (set by public-chat graduation so the first bot in a brand-new
|
|
759
|
+
* workspace can install workflows and invite users).
|
|
760
|
+
*
|
|
761
|
+
* Anything we don't recognize falls back to 'user' so a malformed config
|
|
762
|
+
* can't accidentally elevate a bot.
|
|
179
763
|
*/
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
764
|
+
const ALLOWED_MEMBER_ROLES = ['user', 'admin'];
|
|
765
|
+
function readMemberRoleFromSchemaConfig(fields) {
|
|
766
|
+
const raw = getFieldValue(fields, 'schemaConfig');
|
|
767
|
+
if (typeof raw !== 'string' || raw.length === 0) {
|
|
768
|
+
return 'user';
|
|
183
769
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
return
|
|
190
|
-
}
|
|
191
|
-
catch (error) {
|
|
192
|
-
logger.warn('Failed to load workspace config', { workspaceId, error: String(error) });
|
|
770
|
+
try {
|
|
771
|
+
const parsed = JSON.parse(raw);
|
|
772
|
+
const candidate = parsed?.memberRole;
|
|
773
|
+
if (typeof candidate === 'string' &&
|
|
774
|
+
ALLOWED_MEMBER_ROLES.includes(candidate)) {
|
|
775
|
+
return candidate;
|
|
193
776
|
}
|
|
194
777
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
};
|
|
778
|
+
catch {
|
|
779
|
+
// Falls through — schemaConfig will be reported as unparseable
|
|
780
|
+
// by the main handler later; no need to duplicate the warning.
|
|
781
|
+
}
|
|
782
|
+
return 'user';
|
|
201
783
|
}
|
|
202
784
|
/**
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
* Falls back to direct file I/O if bot-config module not available.
|
|
785
|
+
* Remove a bot entry from a workspace's config and emit a remove event so
|
|
786
|
+
* BotManager can shut the running bot down. Returns the removed entry if any.
|
|
206
787
|
*/
|
|
207
|
-
function
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
788
|
+
async function removeBotFromConfig(workspaceId, activityId) {
|
|
789
|
+
const config = (0, loader_1.loadWorkspaceConfigFile)(workspaceId);
|
|
790
|
+
const idx = config.bots.findIndex((b) => b.activityId === activityId);
|
|
791
|
+
if (idx < 0) {
|
|
792
|
+
return null;
|
|
211
793
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
794
|
+
const [removed] = config.bots.splice(idx, 1);
|
|
795
|
+
(0, loader_1.saveWorkspaceConfigFile)(config);
|
|
796
|
+
(0, loader_1.invalidateConfigCache)();
|
|
797
|
+
await (0, events_1.emitBotEvent)({ workspaceId, bot: removed, action: 'remove' });
|
|
798
|
+
logger.info('Removed bot from config (activity deleted in Hailer)', {
|
|
799
|
+
workspaceId, activityId, email: (0, config_1.maskEmail)(removed.email),
|
|
800
|
+
});
|
|
801
|
+
return removed;
|
|
802
|
+
}
|
|
803
|
+
async function destroyBot(workspaceId, activityId, actor = {}) {
|
|
804
|
+
const config = (0, loader_1.loadWorkspaceConfigFile)(workspaceId);
|
|
805
|
+
const idx = config.bots.findIndex((b) => b.activityId === activityId);
|
|
806
|
+
if (idx < 0) {
|
|
807
|
+
logger.info('destroyBot: no config entry found', { workspaceId, activityId, actor });
|
|
808
|
+
return { removed: null, teardownOk: true };
|
|
216
809
|
}
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
810
|
+
const [removed] = config.bots.splice(idx, 1);
|
|
811
|
+
(0, loader_1.saveWorkspaceConfigFile)(config);
|
|
812
|
+
(0, loader_1.invalidateConfigCache)();
|
|
813
|
+
const emit = await (0, events_1.emitBotEvent)({ workspaceId, bot: removed, action: 'destroy' });
|
|
814
|
+
if (emit.error !== undefined) {
|
|
815
|
+
const errMsg = (0, tool_helpers_1.extractErrorMessage)(emit.error);
|
|
816
|
+
logger.error('Destroyed bot config entry but session teardown failed', {
|
|
817
|
+
workspaceId, activityId, email: (0, config_1.maskEmail)(removed.email),
|
|
818
|
+
actorUid: actor.uid, actorName: actor.name, error: errMsg,
|
|
819
|
+
});
|
|
820
|
+
return { removed, teardownOk: false, teardownError: errMsg };
|
|
821
|
+
}
|
|
822
|
+
if (emit.listenerCount === 0) {
|
|
823
|
+
const errMsg = 'no destroy listener registered (MCP-only mode?)';
|
|
824
|
+
logger.warn('Destroyed bot config entry but no listener ran', {
|
|
825
|
+
workspaceId, activityId, email: (0, config_1.maskEmail)(removed.email),
|
|
826
|
+
actorUid: actor.uid, actorName: actor.name,
|
|
827
|
+
});
|
|
828
|
+
return { removed, teardownOk: false, teardownError: errMsg };
|
|
829
|
+
}
|
|
830
|
+
// NOTE: the Hailer user account is intentionally NOT deleted here — see
|
|
831
|
+
// Bot.destroy() in bot.ts. Hailer's inactive-user prune sweeps them
|
|
832
|
+
// out-of-band. The activity is removed by AI Hub separately.
|
|
833
|
+
logger.info('Destroyed bot (config entry + running session)', {
|
|
834
|
+
workspaceId, activityId, email: (0, config_1.maskEmail)(removed.email),
|
|
835
|
+
actorUid: actor.uid, actorName: actor.name,
|
|
223
836
|
});
|
|
837
|
+
return { removed, teardownOk: true };
|
|
224
838
|
}
|
|
225
839
|
/**
|
|
226
840
|
* Process webhook payload and update workspace config
|
|
227
841
|
*/
|
|
228
|
-
function handleBotConfigWebhook(payload) {
|
|
842
|
+
async function handleBotConfigWebhook(payload) {
|
|
843
|
+
// Serialize ALL webhook processing per activity. Hailer fires multiple
|
|
844
|
+
// webhooks per activity create/phase transition and they hit Express in
|
|
845
|
+
// parallel — without an outer lock the load → modify → save → emit
|
|
846
|
+
// sequence races and bot-manager sees out-of-order add/remove events.
|
|
847
|
+
// Net result: bots not turning on/off reliably under burst traffic.
|
|
848
|
+
return withProvisionLock(payload._id, () => handleBotConfigWebhookLocked(payload));
|
|
849
|
+
}
|
|
850
|
+
async function handleBotConfigWebhookLocked(payload) {
|
|
229
851
|
const workspaceId = payload.cid;
|
|
230
852
|
logger.debug('Processing bot config webhook', {
|
|
231
853
|
activityId: payload._id,
|
|
@@ -233,17 +855,151 @@ function handleBotConfigWebhook(payload) {
|
|
|
233
855
|
workspaceId,
|
|
234
856
|
phase: payload.currentPhase,
|
|
235
857
|
});
|
|
236
|
-
// Extract fields
|
|
858
|
+
// Extract fields. password/userId are reassigned by auto-provisioning below.
|
|
237
859
|
const email = getFieldValue(payload.fields, 'agentEmailInHailer');
|
|
238
|
-
|
|
860
|
+
let password = getFieldValue(payload.fields, 'password');
|
|
239
861
|
const botType = getFieldValue(payload.fields, 'botType');
|
|
240
|
-
|
|
862
|
+
let userId = getFieldValue(payload.fields, 'hailerProfile');
|
|
241
863
|
const schemaConfigStr = getFieldValue(payload.fields, 'schemaConfig');
|
|
242
864
|
const systemPrompt = getFieldValue(payload.fields, 'systemPrompt') || undefined;
|
|
243
865
|
const accessLevel = getFieldValue(payload.fields, 'accessLevel') || undefined;
|
|
866
|
+
const seedContext = getFieldValue(payload.fields, 'seedContext') || undefined;
|
|
244
867
|
// responseMode and allowBotMessages are stored inside schemaConfig JSON, not as separate fields
|
|
245
868
|
let responseMode;
|
|
246
869
|
let allowBotMessages = false;
|
|
870
|
+
let helperMode = false;
|
|
871
|
+
// Parse schemaConfig once, up here, so we can read `adminApiKey` from it
|
|
872
|
+
// before any code path that depends on workspace admin credentials runs
|
|
873
|
+
// (probe path, auto-provisioning, etc.). Reused later for the bot-config
|
|
874
|
+
// update without re-parsing.
|
|
875
|
+
let schemaConfig = null;
|
|
876
|
+
if (schemaConfigStr) {
|
|
877
|
+
try {
|
|
878
|
+
schemaConfig = JSON.parse(schemaConfigStr);
|
|
879
|
+
}
|
|
880
|
+
catch (e) {
|
|
881
|
+
logger.warn('Failed to parse schemaConfig', { schemaConfigStr });
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
// If the activity carries an admin api key in schemaConfig.adminApiKey,
|
|
885
|
+
// validate and persist it now — downstream getAdminApiKey() calls will
|
|
886
|
+
// see it immediately. Safe to run before the probe/provision branches.
|
|
887
|
+
await maybePersistAdminKeyFromSchema(workspaceId, schemaConfig, payload._id, payload.process);
|
|
888
|
+
// Carrier activities exist solely to deliver workspace-scoped config via
|
|
889
|
+
// schemaConfig (admin API key when no real bots exist). Not bots — skip
|
|
890
|
+
// the provisioning + bot-config branches. AI Hub filters them out of the
|
|
891
|
+
// bot list.
|
|
892
|
+
if (schemaConfig?._isCarrier) {
|
|
893
|
+
logger.info('[Webhook] Carrier activity processed — admin key persisted, skipping bot logic', {
|
|
894
|
+
workspaceId,
|
|
895
|
+
activityId: payload._id,
|
|
896
|
+
});
|
|
897
|
+
return {
|
|
898
|
+
success: true,
|
|
899
|
+
action: 'skip-carrier',
|
|
900
|
+
workspaceId,
|
|
901
|
+
botType,
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
// For already-provisioned bots (userId set), probe Hailer to confirm the
|
|
905
|
+
// activity still exists. Hailer queues webhooks during/after delete, so an
|
|
906
|
+
// orphan webhook for a deleted activity would otherwise re-add the bot
|
|
907
|
+
// entry and start a doomed bot. The auto-provision branch below has its
|
|
908
|
+
// own gone-detection — we only need this for the userId-already-set path.
|
|
909
|
+
if (userId) {
|
|
910
|
+
const adminApiKey = (0, workspace_admin_store_1.getAdminApiKey)(workspaceId);
|
|
911
|
+
if (adminApiKey) {
|
|
912
|
+
try {
|
|
913
|
+
const adminClient = await (0, hailer_clients_1.createHailerClientByApiKey)(adminApiKey);
|
|
914
|
+
// Hold the API-key lock across switchEcosystem + probe so a
|
|
915
|
+
// concurrent webhook for a different workspace using the same
|
|
916
|
+
// admin key can't repoint the server context mid-probe.
|
|
917
|
+
const probe = await (0, hailer_clients_1.withApiKeyLock)(adminClient.sessionKey, async () => {
|
|
918
|
+
await adminClient.socket.request(hailer_rpc_1.HailerRpc.Core.switchEcosystem, [workspaceId]);
|
|
919
|
+
return probeActivity(adminClient, payload._id);
|
|
920
|
+
});
|
|
921
|
+
if (probe.gone) {
|
|
922
|
+
const removed = await removeBotFromConfig(workspaceId, payload._id);
|
|
923
|
+
return {
|
|
924
|
+
success: true,
|
|
925
|
+
action: removed ? 'remove' : 'skip',
|
|
926
|
+
workspaceId,
|
|
927
|
+
botType,
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
catch (err) {
|
|
932
|
+
// Probe failures are non-fatal — fall through to the normal flow
|
|
933
|
+
// and let the bot-config update happen as usual. But if the admin
|
|
934
|
+
// key itself was rejected (auth failure), forget it so we don't
|
|
935
|
+
// keep hammering 403 on every webhook.
|
|
936
|
+
await forgetAdminKeyOnAuthFailure(workspaceId, err);
|
|
937
|
+
logger.debug('[Webhook] existence probe failed (non-fatal)', {
|
|
938
|
+
activityId: payload._id,
|
|
939
|
+
error: (0, tool_helpers_1.extractErrorMessage)(err),
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
// Auto-create bot Hailer account if email is set but no profile/password yet.
|
|
945
|
+
// A null result means a sibling webhook is mid-provision but hasn't written
|
|
946
|
+
// the password yet — fall through so the missing-credentials check below skips.
|
|
947
|
+
if (!userId && email) {
|
|
948
|
+
const adminApiKey = (0, workspace_admin_store_1.getAdminApiKey)(workspaceId);
|
|
949
|
+
if (!adminApiKey) {
|
|
950
|
+
logger.warn('[Webhook] No admin credentials for workspace — cannot auto-create bot user', { workspaceId });
|
|
951
|
+
return {
|
|
952
|
+
success: false,
|
|
953
|
+
action: 'skip',
|
|
954
|
+
workspaceId,
|
|
955
|
+
botType,
|
|
956
|
+
error: 'Missing admin credentials for auto-provisioning',
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
try {
|
|
960
|
+
const adminClient = await (0, hailer_clients_1.createHailerClientByApiKey)(adminApiKey);
|
|
961
|
+
const provisioned = await provisionBotUser(adminClient, payload, workspaceId, email);
|
|
962
|
+
if (!provisioned) {
|
|
963
|
+
// null means the activity was deleted, a sibling is mid-provision, or
|
|
964
|
+
// a dangling user blocks register. In every case the payload data is
|
|
965
|
+
// stale or the entry, if any, is in a transient state — do NOT fall
|
|
966
|
+
// through to upsert: that would re-add an entry for a deleted
|
|
967
|
+
// activity (bug seen with queued webhooks for already-deleted bots).
|
|
968
|
+
return {
|
|
969
|
+
success: true,
|
|
970
|
+
action: 'skip',
|
|
971
|
+
workspaceId,
|
|
972
|
+
botType,
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
userId = provisioned.uid;
|
|
976
|
+
password = provisioned.password;
|
|
977
|
+
if (provisioned.freshlyProvisioned) {
|
|
978
|
+
await postProvisioningAudit(adminClient, workspaceId, payload._id, provisioned);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
catch (err) {
|
|
982
|
+
// Auto-provision failure is logged loudly but we return success so
|
|
983
|
+
// Hailer doesn't suspend webhook delivery after consecutive 500s.
|
|
984
|
+
// The activity update is real either way; the operator fixes the
|
|
985
|
+
// underlying cause (permissions, dangling users) and retries by
|
|
986
|
+
// toggling the bot in AI Hub.
|
|
987
|
+
const errMsg = (0, tool_helpers_1.extractErrorMessage)(err);
|
|
988
|
+
await forgetAdminKeyOnAuthFailure(workspaceId, err);
|
|
989
|
+
logger.error('[Webhook] Failed to auto-create bot user (returning 200 to avoid webhook suspension)', {
|
|
990
|
+
err: errMsg,
|
|
991
|
+
email: (0, config_1.maskEmail)(email),
|
|
992
|
+
activityId: payload._id,
|
|
993
|
+
});
|
|
994
|
+
return {
|
|
995
|
+
success: false,
|
|
996
|
+
action: 'skip',
|
|
997
|
+
workspaceId,
|
|
998
|
+
botType,
|
|
999
|
+
error: errMsg,
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
247
1003
|
// Validate required fields
|
|
248
1004
|
if (!email || !password) {
|
|
249
1005
|
logger.warn('Webhook missing credentials', {
|
|
@@ -263,26 +1019,26 @@ function handleBotConfigWebhook(payload) {
|
|
|
263
1019
|
let deployedPhaseId = null;
|
|
264
1020
|
let retiredPhaseId = null;
|
|
265
1021
|
let allowedWorkflows;
|
|
266
|
-
if (
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
allowedWorkflows = schemaConfig.allowedWorkflows;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
catch (e) {
|
|
278
|
-
logger.warn('Failed to parse schemaConfig', { schemaConfigStr });
|
|
1022
|
+
if (schemaConfig) {
|
|
1023
|
+
deployedPhaseId = schemaConfig.deployedPhaseId ?? null;
|
|
1024
|
+
retiredPhaseId = schemaConfig.retiredPhaseId ?? null;
|
|
1025
|
+
responseMode = schemaConfig.responseMode || undefined;
|
|
1026
|
+
allowBotMessages = !!schemaConfig.allowBotMessages;
|
|
1027
|
+
helperMode = !!schemaConfig.helperMode;
|
|
1028
|
+
if (Array.isArray(schemaConfig.allowedWorkflows)) {
|
|
1029
|
+
allowedWorkflows = schemaConfig.allowedWorkflows;
|
|
279
1030
|
}
|
|
280
1031
|
}
|
|
281
|
-
|
|
1032
|
+
// Fail-closed: if we don't know the deployed phase, treat the bot as
|
|
1033
|
+
// disabled. New activities start in the Retired phase, and trailing
|
|
1034
|
+
// webhooks (e.g. from a delete in AI Hub) often arrive with empty
|
|
1035
|
+
// schemaConfig — defaulting to enabled would re-start a bot the user
|
|
1036
|
+
// just disabled.
|
|
1037
|
+
const isDeployed = deployedPhaseId ? payload.currentPhase === deployedPhaseId : false;
|
|
282
1038
|
const isRetired = retiredPhaseId ? payload.currentPhase === retiredPhaseId : false;
|
|
283
1039
|
const enabled = isDeployed && !isRetired;
|
|
284
|
-
|
|
285
|
-
const
|
|
1040
|
+
const config = (0, loader_1.loadWorkspaceConfigFile)(workspaceId);
|
|
1041
|
+
const existingEntry = config.bots.find((b) => b.activityId === payload._id);
|
|
286
1042
|
const botEntry = {
|
|
287
1043
|
activityId: payload._id,
|
|
288
1044
|
userId: userId || null,
|
|
@@ -296,12 +1052,28 @@ function handleBotConfigWebhook(payload) {
|
|
|
296
1052
|
allowedWorkflows,
|
|
297
1053
|
allowBotMessages,
|
|
298
1054
|
responseMode,
|
|
1055
|
+
helperMode,
|
|
1056
|
+
// Preserve the previously-stored seedContext if Hailer omitted the
|
|
1057
|
+
// field from this webhook (it only re-sends fields that changed in
|
|
1058
|
+
// the phase event, so subsequent transitions can wipe it).
|
|
1059
|
+
seedContext: seedContext ?? existingEntry?.seedContext,
|
|
1060
|
+
introPosted: existingEntry?.introPosted,
|
|
299
1061
|
};
|
|
300
|
-
|
|
301
|
-
|
|
1062
|
+
const existingIndex = existingEntry
|
|
1063
|
+
? config.bots.indexOf(existingEntry)
|
|
1064
|
+
: -1;
|
|
302
1065
|
let action;
|
|
303
1066
|
if (existingIndex >= 0) {
|
|
304
|
-
|
|
1067
|
+
// Carry forward "this bot has already done X" flags. Without this, a
|
|
1068
|
+
// phase toggle (e.g. Retire → Deploy) reconstructs the entry from the
|
|
1069
|
+
// webhook payload alone and the bot re-runs its one-shot self-intro
|
|
1070
|
+
// — which also re-toggles its activity-discussion membership and
|
|
1071
|
+
// produces a "left → joined" sequence in the UI.
|
|
1072
|
+
const existing = config.bots[existingIndex];
|
|
1073
|
+
config.bots[existingIndex] = {
|
|
1074
|
+
...botEntry,
|
|
1075
|
+
introPosted: existing.introPosted,
|
|
1076
|
+
};
|
|
305
1077
|
action = enabled ? 'update' : 'remove';
|
|
306
1078
|
}
|
|
307
1079
|
else if (enabled) {
|
|
@@ -312,45 +1084,18 @@ function handleBotConfigWebhook(payload) {
|
|
|
312
1084
|
action = 'update';
|
|
313
1085
|
}
|
|
314
1086
|
logger.info('Updated bot', { workspaceId, email: (0, config_1.maskEmail)(email), botType, enabled, action });
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
if (invalidateConfigCache)
|
|
319
|
-
invalidateConfigCache();
|
|
320
|
-
// Trigger callback for hot reload
|
|
321
|
-
if (botUpdateCallback) {
|
|
322
|
-
botUpdateCallback(workspaceId, botEntry, action);
|
|
323
|
-
}
|
|
1087
|
+
(0, loader_1.saveWorkspaceConfigFile)(config);
|
|
1088
|
+
(0, loader_1.invalidateConfigCache)();
|
|
1089
|
+
await (0, events_1.emitBotEvent)({ workspaceId, bot: botEntry, action });
|
|
324
1090
|
return {
|
|
325
1091
|
success: true,
|
|
326
1092
|
action,
|
|
327
1093
|
workspaceId,
|
|
328
1094
|
botType,
|
|
1095
|
+
userId: userId || null,
|
|
329
1096
|
};
|
|
330
1097
|
}
|
|
331
|
-
/**
|
|
332
|
-
* List all workspace configs.
|
|
333
|
-
* Delegates to bot-config/loader when available.
|
|
334
|
-
*/
|
|
335
1098
|
function listWorkspaceConfigs() {
|
|
336
|
-
|
|
337
|
-
return _listConfigFiles();
|
|
338
|
-
}
|
|
339
|
-
// Fallback for MCP-only installs
|
|
340
|
-
const configDir = constants_1.BOT_CONFIG_DIR;
|
|
341
|
-
if (!fs.existsSync(configDir))
|
|
342
|
-
return [];
|
|
343
|
-
const files = fs.readdirSync(configDir).filter((f) => f.endsWith('.json'));
|
|
344
|
-
const configs = [];
|
|
345
|
-
for (const file of files) {
|
|
346
|
-
try {
|
|
347
|
-
const content = fs.readFileSync(path.join(configDir, file), 'utf-8');
|
|
348
|
-
configs.push(JSON.parse(content));
|
|
349
|
-
}
|
|
350
|
-
catch (error) {
|
|
351
|
-
logger.warn('Failed to load workspace config', { file, error: String(error) });
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
return configs;
|
|
1099
|
+
return (0, loader_1.listWorkspaceConfigFiles)();
|
|
355
1100
|
}
|
|
356
1101
|
//# sourceMappingURL=webhook-handler.js.map
|