@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
package/dist/bot/bot.js
CHANGED
|
@@ -9,13 +9,49 @@
|
|
|
9
9
|
* - buildSystemPrompt: LLM system prompt construction
|
|
10
10
|
* - ConversationManager, MessageClassifier, MessageFormatterService, etc.
|
|
11
11
|
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
12
45
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
46
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
47
|
};
|
|
15
48
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.Bot = void 0;
|
|
49
|
+
exports.Bot = exports.ActivityGoneError = void 0;
|
|
50
|
+
const v8 = __importStar(require("v8"));
|
|
17
51
|
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
18
52
|
const tool_registry_1 = require("../mcp/tool-registry");
|
|
53
|
+
const tool_profiles_1 = require("../mcp/tool-profiles");
|
|
54
|
+
const hailer_rpc_1 = require("../mcp/hailer-rpc");
|
|
19
55
|
const hailer_clients_1 = require("../mcp/hailer-clients");
|
|
20
56
|
const hailer_api_client_1 = require("../mcp/utils/hailer-api-client");
|
|
21
57
|
const types_1 = require("../mcp/utils/types");
|
|
@@ -24,43 +60,190 @@ const tool_executor_1 = require("./tool-executor");
|
|
|
24
60
|
const workspace_overview_1 = require("./workspace-overview");
|
|
25
61
|
const operation_logger_1 = require("./operation-logger");
|
|
26
62
|
const services_1 = require("./services");
|
|
63
|
+
const bot_config_1 = require("../bot-config");
|
|
27
64
|
const logger_1 = require("../lib/logger");
|
|
65
|
+
const config_1 = require("../config");
|
|
28
66
|
// ===== CONSTANTS =====
|
|
67
|
+
/**
|
|
68
|
+
* Parse the optional `seedContext` field on the bot config — a JSON array
|
|
69
|
+
* of prior chat turns from the public demo — into Anthropic message
|
|
70
|
+
* params suitable for prepending to the self-intro request. Caps the
|
|
71
|
+
* count so a misbehaving caller can't blow the context budget.
|
|
72
|
+
*
|
|
73
|
+
* Returns `[]` for missing, malformed, or empty input; the bot falls
|
|
74
|
+
* back to the generic intro path in that case.
|
|
75
|
+
*/
|
|
76
|
+
const SEED_TURNS_MAX = 24;
|
|
77
|
+
function parseSeedTurns(raw, log) {
|
|
78
|
+
const empty = { turns: [], viewedSlides: [] };
|
|
79
|
+
if (!raw) {
|
|
80
|
+
return empty;
|
|
81
|
+
}
|
|
82
|
+
let parsed;
|
|
83
|
+
try {
|
|
84
|
+
parsed = JSON.parse(raw);
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
log.warn('seedContext: JSON parse failed — skipping', {
|
|
88
|
+
err: err instanceof Error ? err.message : String(err),
|
|
89
|
+
});
|
|
90
|
+
return empty;
|
|
91
|
+
}
|
|
92
|
+
// Two supported shapes:
|
|
93
|
+
// - bare array of {role, content} turns (legacy)
|
|
94
|
+
// - object { turns: [...], viewedSlides: [...] } (current)
|
|
95
|
+
let turnSource;
|
|
96
|
+
let viewedSlides = [];
|
|
97
|
+
if (Array.isArray(parsed)) {
|
|
98
|
+
turnSource = parsed;
|
|
99
|
+
}
|
|
100
|
+
else if (parsed && typeof parsed === 'object') {
|
|
101
|
+
turnSource = parsed.turns;
|
|
102
|
+
const slidesRaw = parsed.viewedSlides;
|
|
103
|
+
if (Array.isArray(slidesRaw)) {
|
|
104
|
+
viewedSlides = slidesRaw.filter((id) => typeof id === 'string' && id.length > 0);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (!Array.isArray(turnSource)) {
|
|
108
|
+
return { turns: [], viewedSlides };
|
|
109
|
+
}
|
|
110
|
+
const turns = [];
|
|
111
|
+
for (const item of turnSource) {
|
|
112
|
+
if (!item || typeof item !== 'object') {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const role = item.role;
|
|
116
|
+
const content = item.content;
|
|
117
|
+
if ((role !== 'user' && role !== 'assistant') || typeof content !== 'string' || !content) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
turns.push({ role, content });
|
|
121
|
+
if (turns.length >= SEED_TURNS_MAX) {
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return { turns, viewedSlides };
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Thrown by Bot.start() when the bot's own Agent Directory activity has been
|
|
129
|
+
* deleted in Hailer. BotManager catches this and discards the bot without
|
|
130
|
+
* adding it to the running set; the entry has already been removed from
|
|
131
|
+
* .bot-config/{workspaceId}.json by the time this is thrown.
|
|
132
|
+
*/
|
|
133
|
+
class ActivityGoneError extends Error {
|
|
134
|
+
activityId;
|
|
135
|
+
workspaceId;
|
|
136
|
+
constructor(activityId, workspaceId) {
|
|
137
|
+
super(`Activity ${activityId} no longer exists in workspace ${workspaceId}`);
|
|
138
|
+
this.activityId = activityId;
|
|
139
|
+
this.workspaceId = workspaceId;
|
|
140
|
+
this.name = 'ActivityGoneError';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
exports.ActivityGoneError = ActivityGoneError;
|
|
29
144
|
const WRITE_TOOLS = new Set(['create_activity', 'update_activity']);
|
|
30
145
|
const DISCUSSION_TOOLS = new Set([
|
|
31
146
|
'fetch_discussion_messages', 'fetch_previous_discussion_messages',
|
|
32
147
|
'add_discussion_message', 'invite_discussion_members', 'get_activity_from_discussion',
|
|
33
148
|
]);
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
'create_activity', 'update_activity',
|
|
39
|
-
'install_workflow',
|
|
40
|
-
'list_my_discussions', 'fetch_discussion_messages', 'fetch_previous_discussion_messages',
|
|
41
|
-
'add_discussion_message', 'join_discussion', 'leave_discussion',
|
|
42
|
-
'invite_discussion_members', 'get_activity_from_discussion',
|
|
43
|
-
'upload_files', 'download_file',
|
|
44
|
-
'list_insights', 'get_insight_data', 'preview_insight', 'create_insight', 'update_insight',
|
|
45
|
-
'list_workflow_permissions', 'check_user_permissions',
|
|
46
|
-
'grant_workflow_permission', 'revoke_workflow_permission',
|
|
47
|
-
'resolve_date',
|
|
48
|
-
]);
|
|
49
|
-
const MODEL_HAIKU = 'claude-haiku-4-5-20251001';
|
|
50
|
-
const MODEL_SONNET = 'claude-sonnet-4-5-20250929';
|
|
149
|
+
const MODEL_HAIKU = 'claude-haiku-4-5';
|
|
150
|
+
const MODEL_SONNET = 'claude-sonnet-4-6';
|
|
151
|
+
const MAX_TOOL_RESULT_CHARS = 40000;
|
|
152
|
+
const MAX_PROMPT_TRIM_ATTEMPTS = 5;
|
|
51
153
|
const IMAGE_MIME_TYPES = new Set(['image/jpeg', 'image/png', 'image/gif', 'image/webp']);
|
|
52
154
|
const DOCUMENT_MIME_TYPES = new Set(['application/pdf']);
|
|
53
155
|
const TEXT_MIME_TYPES = new Set(['text/plain', 'text/csv', 'text/html', 'text/markdown', 'application/json', 'application/xml']);
|
|
156
|
+
const EXCEL_MIME_TYPES = new Set([
|
|
157
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx only
|
|
158
|
+
]);
|
|
159
|
+
const WORD_MIME_TYPES = new Set([
|
|
160
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document', // .docx
|
|
161
|
+
]);
|
|
54
162
|
const MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
163
|
+
const MAX_OFFICE_FILE_SIZE = 10 * 1024 * 1024;
|
|
164
|
+
const OFFICE_TEXT_CAP = 512 * 1024;
|
|
165
|
+
const HEAP_BACKPRESSURE_THRESHOLD = 0.7;
|
|
166
|
+
function fileSizeLimit(mime) {
|
|
167
|
+
return EXCEL_MIME_TYPES.has(mime) || WORD_MIME_TYPES.has(mime) ? MAX_OFFICE_FILE_SIZE : MAX_FILE_SIZE;
|
|
168
|
+
}
|
|
169
|
+
const ATTACHMENT_KINDS = [
|
|
170
|
+
{ label: 'Image', trailing: 'included below for vision', matches: (mimeType) => IMAGE_MIME_TYPES.has(mimeType) },
|
|
171
|
+
{ label: 'Document', trailing: 'included below', matches: (mimeType) => DOCUMENT_MIME_TYPES.has(mimeType) || TEXT_MIME_TYPES.has(mimeType) },
|
|
172
|
+
{ label: 'Spreadsheet', trailing: 'included below as CSV per sheet', matches: (mimeType) => EXCEL_MIME_TYPES.has(mimeType) },
|
|
173
|
+
{ label: 'Word document', trailing: 'included below as plain text', matches: (mimeType) => WORD_MIME_TYPES.has(mimeType) },
|
|
174
|
+
];
|
|
175
|
+
function describeAttachment(mime, filename, fileId) {
|
|
176
|
+
const matched = ATTACHMENT_KINDS.find((kind) => kind.matches(mime));
|
|
177
|
+
if (!matched) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
return `[${matched.label} attached: ${filename} (fileId: ${fileId}) — ${matched.trailing}]`;
|
|
181
|
+
}
|
|
182
|
+
function isSupportedMime(mime) {
|
|
183
|
+
return IMAGE_MIME_TYPES.has(mime) || DOCUMENT_MIME_TYPES.has(mime) || TEXT_MIME_TYPES.has(mime)
|
|
184
|
+
|| EXCEL_MIME_TYPES.has(mime) || WORD_MIME_TYPES.has(mime);
|
|
185
|
+
}
|
|
186
|
+
function isOfficeMime(mime) {
|
|
187
|
+
return EXCEL_MIME_TYPES.has(mime) || WORD_MIME_TYPES.has(mime);
|
|
188
|
+
}
|
|
189
|
+
function capText(text) {
|
|
190
|
+
return text.length > OFFICE_TEXT_CAP ? text.slice(0, OFFICE_TEXT_CAP) + '\n\n[...content truncated...]' : text;
|
|
191
|
+
}
|
|
192
|
+
function isHeapUnderPressure() {
|
|
193
|
+
const { heapUsed } = process.memoryUsage();
|
|
194
|
+
const heapLimit = v8.getHeapStatistics().heap_size_limit;
|
|
195
|
+
return heapUsed / heapLimit > HEAP_BACKPRESSURE_THRESHOLD;
|
|
196
|
+
}
|
|
197
|
+
function cellToCsv(value) {
|
|
198
|
+
if (value === null || value === undefined) {
|
|
199
|
+
return '';
|
|
200
|
+
}
|
|
201
|
+
if (value instanceof Date) {
|
|
202
|
+
return value.toISOString();
|
|
203
|
+
}
|
|
204
|
+
if (typeof value === 'object') {
|
|
205
|
+
const v = value;
|
|
206
|
+
if (typeof v.result !== 'undefined') {
|
|
207
|
+
return cellToCsv(v.result);
|
|
208
|
+
}
|
|
209
|
+
if (typeof v.text === 'string') {
|
|
210
|
+
return cellToCsv(v.text);
|
|
211
|
+
}
|
|
212
|
+
if (Array.isArray(v.richText)) {
|
|
213
|
+
return v.richText.map((rt) => rt.text || '').join('');
|
|
214
|
+
}
|
|
215
|
+
if (typeof v.error === 'string') {
|
|
216
|
+
return v.error;
|
|
217
|
+
}
|
|
218
|
+
return '';
|
|
219
|
+
}
|
|
220
|
+
const s = String(value);
|
|
221
|
+
return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
|
|
222
|
+
}
|
|
223
|
+
// Global mutex — serialize Office downloads + parses across the pod to bound peak memory
|
|
224
|
+
let officeParseQueue = Promise.resolve();
|
|
225
|
+
function withOfficeParseLock(fn) {
|
|
226
|
+
const next = officeParseQueue.then(fn, fn);
|
|
227
|
+
officeParseQueue = next.catch(() => undefined);
|
|
228
|
+
return next;
|
|
229
|
+
}
|
|
230
|
+
const SONNET_ROUTE = { model: MODEL_SONNET, maxTokens: 16384 };
|
|
55
231
|
const TOOL_STATUS_LABELS = {
|
|
56
|
-
list_activities: 'Searching activities',
|
|
232
|
+
list_activities: 'Searching activities',
|
|
57
233
|
show_activity_by_id: 'Reading activity', create_activity: 'Creating activity',
|
|
58
|
-
update_activity: 'Updating activity',
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
234
|
+
update_activity: 'Updating activity',
|
|
235
|
+
describe_workflows: 'Reading workflows',
|
|
236
|
+
update_workflow_structure: 'Updating workflow structure',
|
|
237
|
+
run_insight: 'Running report', save_insight: 'Saving report',
|
|
238
|
+
search_workspace_users: 'Searching users',
|
|
62
239
|
fetch_discussion_messages: 'Reading messages', add_discussion_message: 'Sending message',
|
|
63
240
|
join_discussion: 'Joining discussion',
|
|
241
|
+
query_events: 'Loading events', manage_calendar: 'Managing calendars',
|
|
242
|
+
create_event: 'Creating event',
|
|
243
|
+
update_event: 'Updating event', delete_event: 'Deleting event',
|
|
244
|
+
respond_to_invite: 'Responding to invite',
|
|
245
|
+
set_workflow_permission: 'Updating workflow access',
|
|
246
|
+
manage_my_profile: 'Updating my profile',
|
|
64
247
|
};
|
|
65
248
|
// ===== BOT CLASS =====
|
|
66
249
|
class Bot {
|
|
@@ -97,37 +280,47 @@ class Bot {
|
|
|
97
280
|
workspaceUpdatedHandler = null;
|
|
98
281
|
cacheInvalidateHandler = null;
|
|
99
282
|
_connected = false;
|
|
100
|
-
// Config
|
|
101
283
|
config;
|
|
284
|
+
anthropicApiKey;
|
|
102
285
|
botManager;
|
|
103
|
-
constructor(config) {
|
|
286
|
+
constructor(config, runtime) {
|
|
104
287
|
this.config = {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
accessLevel: config.accessLevel || 'all',
|
|
111
|
-
allowedWorkflows: config.allowedWorkflows || [],
|
|
112
|
-
allowBotMessages: config.allowBotMessages || false,
|
|
288
|
+
...config,
|
|
289
|
+
accessLevel: config.accessLevel ?? 'all',
|
|
290
|
+
allowedWorkflows: config.allowedWorkflows ?? [],
|
|
291
|
+
allowBotMessages: config.allowBotMessages ?? false,
|
|
292
|
+
helperMode: config.helperMode ?? false,
|
|
113
293
|
};
|
|
114
|
-
this.
|
|
294
|
+
this.anthropicApiKey = runtime.anthropicApiKey;
|
|
295
|
+
this.botManager = runtime.botManager ?? null;
|
|
115
296
|
this._systemPrompt = config.systemPrompt;
|
|
116
|
-
this._responseMode = config.responseMode
|
|
297
|
+
this._responseMode = config.responseMode ?? 'always';
|
|
117
298
|
this.logger = (0, logger_1.createLogger)({ component: 'Bot' });
|
|
118
299
|
this.clientManager = new hailer_clients_1.HailerClientManager(config.apiHost, config.email, config.password);
|
|
119
|
-
this.toolExecutor = new tool_executor_1.ToolExecutor(
|
|
300
|
+
this.toolExecutor = new tool_executor_1.ToolExecutor(runtime.toolRegistry);
|
|
120
301
|
this.signalRouter = new services_1.SignalRouter(this.logger);
|
|
121
302
|
this.permissions = new services_1.BotPermissions(this.config.allowedWorkflows, this.logger);
|
|
122
303
|
this.workspaceRefresh = new services_1.WorkspaceRefresh(this.logger);
|
|
123
304
|
}
|
|
124
305
|
// Public accessors (used by BotManager for hot-reload)
|
|
125
|
-
get email() {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
get
|
|
129
|
-
|
|
130
|
-
|
|
306
|
+
get email() {
|
|
307
|
+
return this.config.email;
|
|
308
|
+
}
|
|
309
|
+
get password() {
|
|
310
|
+
return this.config.password;
|
|
311
|
+
}
|
|
312
|
+
get accessLevel() {
|
|
313
|
+
return this.config.accessLevel;
|
|
314
|
+
}
|
|
315
|
+
get connected() {
|
|
316
|
+
return this._connected;
|
|
317
|
+
}
|
|
318
|
+
get workspaceId() {
|
|
319
|
+
return this._workspaceId;
|
|
320
|
+
}
|
|
321
|
+
get botUserId() {
|
|
322
|
+
return this.userId;
|
|
323
|
+
}
|
|
131
324
|
updateSystemPrompt(prompt) {
|
|
132
325
|
this._systemPrompt = prompt;
|
|
133
326
|
this.logger.info('System prompt updated live', { hasPrompt: !!prompt });
|
|
@@ -136,21 +329,88 @@ class Bot {
|
|
|
136
329
|
this._responseMode = mode || 'always';
|
|
137
330
|
this.logger.info('Response mode updated live', { responseMode: this._responseMode });
|
|
138
331
|
}
|
|
332
|
+
updateHelperMode(helperMode) {
|
|
333
|
+
this.config.helperMode = !!helperMode;
|
|
334
|
+
this.logger.info('Helper mode updated live', { helperMode: this.config.helperMode });
|
|
335
|
+
}
|
|
139
336
|
// ===== LIFECYCLE =====
|
|
140
337
|
async start() {
|
|
141
|
-
if (this._connected)
|
|
338
|
+
if (this._connected) {
|
|
142
339
|
return;
|
|
340
|
+
}
|
|
143
341
|
this.logger.debug('Starting bot', { email: this.config.email });
|
|
144
342
|
// 1. Connect
|
|
145
343
|
this.client = await this.clientManager.connect();
|
|
146
344
|
this.config.password = ''; // Clear from memory — connection is established
|
|
147
345
|
// 2. Fetch workspace data + user in one call
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
346
|
+
const initFields = ['user', 'processes', 'users', 'network', 'networks', 'teams', 'groups'];
|
|
347
|
+
this.init = await this.client.socket.request(hailer_rpc_1.HailerRpc.Core.init, [initFields]);
|
|
348
|
+
try {
|
|
349
|
+
const pendingInvites = await this.client.socket.request(hailer_rpc_1.HailerRpc.User.inviteList, []);
|
|
350
|
+
if (pendingInvites?.length) {
|
|
351
|
+
for (const invite of pendingInvites) {
|
|
352
|
+
await this.client.socket.request(hailer_rpc_1.HailerRpc.User.inviteAccept, [invite.invite_key]);
|
|
353
|
+
await this.client.socket.request(hailer_rpc_1.HailerRpc.Core.switchEcosystem, [invite.cid]);
|
|
354
|
+
this.logger.info('Accepted workspace invite', { workspaceId: invite.cid, workspaceName: invite.network?.name });
|
|
355
|
+
}
|
|
356
|
+
this.init = await this.client.socket.request(hailer_rpc_1.HailerRpc.Core.init, [initFields]);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
catch (err) {
|
|
360
|
+
this.logger.warn('Failed to check/accept pending invites', { error: err instanceof Error ? err.message : err });
|
|
361
|
+
}
|
|
362
|
+
// Hailer drops password-auth users into their personal "My Hailer" ecosystem
|
|
363
|
+
// on each fresh login, even after they've accepted a workspace invite. Force
|
|
364
|
+
// the configured workspace as the active ecosystem so the bot operates where
|
|
365
|
+
// it was provisioned, not in its personal workspace.
|
|
366
|
+
if (this.config.workspaceId && this.init.network?._id !== this.config.workspaceId) {
|
|
367
|
+
const fromWorkspaceId = this.init.network?._id;
|
|
368
|
+
try {
|
|
369
|
+
await this.client.socket.request(hailer_rpc_1.HailerRpc.Core.switchEcosystem, [this.config.workspaceId]);
|
|
370
|
+
this.init = await this.client.socket.request(hailer_rpc_1.HailerRpc.Core.init, [initFields]);
|
|
371
|
+
this.logger.info('Switched to configured workspace', {
|
|
372
|
+
from: fromWorkspaceId, to: this.config.workspaceId,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
catch (err) {
|
|
376
|
+
this.logger.warn('Failed to switch to configured workspace', {
|
|
377
|
+
workspaceId: this.config.workspaceId,
|
|
378
|
+
error: err instanceof Error ? err.message : err,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// Self-prune: probe the bot's own Agent Directory activity. If the
|
|
383
|
+
// activity is gone (definitively deleted OR otherwise unreachable in
|
|
384
|
+
// a way that matches the gone signals), drop the local config entry
|
|
385
|
+
// so we stop trying to run this bot. The bot's Hailer user account
|
|
386
|
+
// is NEVER deleted from here — a previous version did, and the
|
|
387
|
+
// classifier shared a numeric code with "Permission denied" which
|
|
388
|
+
// caused mass user-account deletion on transient permission errors.
|
|
389
|
+
// Orphaned bot users are reaped separately by Hailer's inactive-user
|
|
390
|
+
// prune (out-of-band).
|
|
391
|
+
if (this.config.activityId && this.config.workspaceId) {
|
|
392
|
+
try {
|
|
393
|
+
await this.client.socket.request(hailer_rpc_1.HailerRpc.Activities.load, [this.config.activityId]);
|
|
394
|
+
}
|
|
395
|
+
catch (err) {
|
|
396
|
+
const shape = (0, bot_config_1.extractHailerError)(err);
|
|
397
|
+
if ((0, bot_config_1.isActivityGone)(shape)) {
|
|
398
|
+
this.logger.info('Self-probe: activity gone — pruning local config entry (keeping Hailer user)', {
|
|
399
|
+
activityId: this.config.activityId, workspaceId: this.config.workspaceId,
|
|
400
|
+
msg: shape.msg, code: shape.code,
|
|
401
|
+
});
|
|
402
|
+
this.pruneSelfFromConfig();
|
|
403
|
+
throw new ActivityGoneError(this.config.activityId, this.config.workspaceId);
|
|
404
|
+
}
|
|
405
|
+
this.logger.debug('Self-probe non-deletion error — continuing startup', {
|
|
406
|
+
activityId: this.config.activityId, msg: shape.msg, code: shape.code,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
}
|
|
151
410
|
this.userId = this.init.user?._id || '';
|
|
152
|
-
if (!this.userId)
|
|
411
|
+
if (!this.userId) {
|
|
153
412
|
throw new Error('Could not determine bot user ID');
|
|
413
|
+
}
|
|
154
414
|
(0, types_1.normalizeInitProcesses)(this.init);
|
|
155
415
|
this._workspaceId = this.init.network?._id || this.config.workspaceId;
|
|
156
416
|
// 3. Build caches
|
|
@@ -172,7 +432,7 @@ class Bot {
|
|
|
172
432
|
workflowNames: this.init.processes.map(p => p.name),
|
|
173
433
|
});
|
|
174
434
|
// 4. Anthropic client
|
|
175
|
-
this.anthropic = new sdk_1.default({ apiKey: this.
|
|
435
|
+
this.anthropic = new sdk_1.default({ apiKey: this.anthropicApiKey });
|
|
176
436
|
// 5. Services
|
|
177
437
|
const botConnection = {
|
|
178
438
|
client: this.client,
|
|
@@ -194,11 +454,13 @@ class Bot {
|
|
|
194
454
|
email: this.config.email, password: '', // Intentionally empty — cleared after connect
|
|
195
455
|
workspaceRoles: { [currentWorkspaceId]: 'admin' },
|
|
196
456
|
currentWorkspaceId, allowedGroups: [tool_registry_1.ToolGroup.READ, tool_registry_1.ToolGroup.WRITE, tool_registry_1.ToolGroup.BOT_INTERNAL],
|
|
457
|
+
botActivityId: this.config.activityId,
|
|
197
458
|
};
|
|
198
459
|
// 7. Wire workspace refresh
|
|
199
460
|
this.workspaceRefresh.setHandler(async (scopes) => {
|
|
200
|
-
if (!this.client || !this.init)
|
|
461
|
+
if (!this.client || !this.init) {
|
|
201
462
|
return;
|
|
463
|
+
}
|
|
202
464
|
const result = await services_1.WorkspaceRefresh.refresh(this.client, scopes, this.init, this.config.workspaceId, this.permissions, this.userContext, this.logger);
|
|
203
465
|
this.init = result.init;
|
|
204
466
|
this.workspaceCache = result.workspaceCache;
|
|
@@ -206,10 +468,17 @@ class Bot {
|
|
|
206
468
|
this._workspaceId = result.workspaceId;
|
|
207
469
|
});
|
|
208
470
|
// 8. Subscribe to signals
|
|
209
|
-
this.signalHandler = (data) =>
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
471
|
+
this.signalHandler = (data) => {
|
|
472
|
+
this.logger.info('Bot received messenger.new signal', {
|
|
473
|
+
discussion: data.discussion,
|
|
474
|
+
uid: data.uid,
|
|
475
|
+
msg_id: data.msg_id,
|
|
476
|
+
});
|
|
477
|
+
this.handleSignal({
|
|
478
|
+
type: 'messenger.new', data, timestamp: Date.now(),
|
|
479
|
+
workspaceId: data.sid,
|
|
480
|
+
});
|
|
481
|
+
};
|
|
213
482
|
this.clientManager.onSignal('messenger.new', this.signalHandler);
|
|
214
483
|
this.processUpdatedHandler = () => this.workspaceRefresh.schedule('processes');
|
|
215
484
|
this.workspaceUpdatedHandler = () => this.workspaceRefresh.schedule('network');
|
|
@@ -219,6 +488,285 @@ class Bot {
|
|
|
219
488
|
this.clientManager.onSignal('cache.invalidate', this.cacheInvalidateHandler);
|
|
220
489
|
this._connected = true;
|
|
221
490
|
this.logger.debug('Bot started', { userId: this.userId, workspaceId: this._workspaceId });
|
|
491
|
+
// Fire-and-forget: re-assert that the bot is following its own
|
|
492
|
+
// activity discussion on every startup. The self-intro path used to be
|
|
493
|
+
// the only caller of joinActivityDiscussion, but it short-circuits on
|
|
494
|
+
// `introPosted: true`, leaving bots that were unfollowed in the past
|
|
495
|
+
// (e.g. by the pre-fix raw activities.follow toggle, or by any other
|
|
496
|
+
// path that flipped their follow state) silently subscribed to nothing.
|
|
497
|
+
// Running joinActivityDiscussion here closes that loop — it's
|
|
498
|
+
// idempotent (no-op if already following).
|
|
499
|
+
this.ensureFollowingOwnDiscussion().catch((err) => {
|
|
500
|
+
const { msg, code } = (0, bot_config_1.extractHailerError)(err);
|
|
501
|
+
this.logger.warn('ensureFollowingOwnDiscussion threw (non-fatal)', { msg, code });
|
|
502
|
+
});
|
|
503
|
+
// Fire-and-forget self-introduction. One-shot per bot (persisted via
|
|
504
|
+
// BotEntry.introPosted), so restarts don't re-spam the discussion. Errors
|
|
505
|
+
// are logged inside the method — this catch is only a safety net.
|
|
506
|
+
this.introduceSelfIfNeeded().catch((err) => {
|
|
507
|
+
const { msg, code } = (0, bot_config_1.extractHailerError)(err);
|
|
508
|
+
this.logger.warn('Self-intro threw (non-fatal)', { msg, code });
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Re-join the bot's own activity discussion if (and only if) the bot has
|
|
513
|
+
* drifted out of it. Runs every startup so bots that lost their follow
|
|
514
|
+
* state (e.g. via the pre-51254a2 raw activities.follow toggle) recover.
|
|
515
|
+
*
|
|
516
|
+
* Reads the activity's `followers` list and skips the join when the bot's
|
|
517
|
+
* userId is already there. activities.follow is a toggle that emits a
|
|
518
|
+
* leave/join event on every call — even toggle-toggle pairs — so calling
|
|
519
|
+
* unconditionally pollutes the discussion with "left / joined" pairs on
|
|
520
|
+
* every restart (visible in the timeline after deploy → retire → deploy).
|
|
521
|
+
*/
|
|
522
|
+
async ensureFollowingOwnDiscussion() {
|
|
523
|
+
if (!this.config.activityId || !this.hailerApi || !this.userId) {
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
try {
|
|
527
|
+
const activity = await this.hailerApi.fetchActivityById(this.config.activityId);
|
|
528
|
+
const followers = Array.isArray(activity?.followers) ? activity.followers : [];
|
|
529
|
+
if (followers.includes(this.userId)) {
|
|
530
|
+
this.logger.debug('Already following own discussion — skipping toggle', {
|
|
531
|
+
activityId: this.config.activityId,
|
|
532
|
+
});
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
await this.hailerApi.joinActivityDiscussion(this.config.activityId);
|
|
536
|
+
}
|
|
537
|
+
catch (err) {
|
|
538
|
+
const { msg, code } = (0, bot_config_1.extractHailerError)(err);
|
|
539
|
+
this.logger.debug('ensureFollowingOwnDiscussion failed (non-fatal)', { msg, code });
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Post a one-time greeting in the bot's own activity discussion explaining
|
|
544
|
+
* what users can ask the bot to change about itself (system prompt, etc.).
|
|
545
|
+
* Marks the entry's `introPosted` flag in `.bot-config` so subsequent
|
|
546
|
+
* restarts don't repeat the message.
|
|
547
|
+
*
|
|
548
|
+
* Follow state is handled separately by ensureFollowingOwnDiscussion()
|
|
549
|
+
* which runs on every startup. We don't flip `introPosted` until the send
|
|
550
|
+
* succeeds, so a permission failure on first start retries on the next
|
|
551
|
+
* start instead of silently giving up.
|
|
552
|
+
*/
|
|
553
|
+
async introduceSelfIfNeeded() {
|
|
554
|
+
if (!this.config.activityId || !this.config.workspaceId || !this.client) {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
const cfg = (0, bot_config_1.loadWorkspaceConfigFile)(this.config.workspaceId);
|
|
558
|
+
const entry = cfg.bots.find((b) => b.activityId === this.config.activityId);
|
|
559
|
+
if (!entry) {
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (entry.introPosted) {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
let discussionId;
|
|
566
|
+
try {
|
|
567
|
+
const activity = await this.client.socket.request(hailer_rpc_1.HailerRpc.Activities.load, [this.config.activityId]);
|
|
568
|
+
discussionId = activity?.discussion;
|
|
569
|
+
}
|
|
570
|
+
catch (err) {
|
|
571
|
+
const { msg, code } = (0, bot_config_1.extractHailerError)(err);
|
|
572
|
+
this.logger.warn('Self-intro: activities.load failed', { msg, code });
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
if (!discussionId) {
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
if (!this.anthropic) {
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
// Generate the intro via LLM so each bot speaks in its own voice. The
|
|
582
|
+
// bot's system prompt already explains its self-management capabilities,
|
|
583
|
+
// so a "pirate" bot greets in pirate voice while still mentioning users
|
|
584
|
+
// can change it.
|
|
585
|
+
const systemPrompt = (0, services_1.buildSystemPrompt)({
|
|
586
|
+
init: this.init, userId: this.userId,
|
|
587
|
+
workspaceOverview: this.workspaceOverview, customPrompt: this._systemPrompt,
|
|
588
|
+
helperMode: this.config.helperMode,
|
|
589
|
+
hasSeedContext: !!this.config.seedContext,
|
|
590
|
+
responseMode: this._responseMode,
|
|
591
|
+
accessLevel: this.config.accessLevel,
|
|
592
|
+
allowBotMessages: this.config.allowBotMessages,
|
|
593
|
+
});
|
|
594
|
+
const { turns: seedTurns, viewedSlides } = parseSeedTurns(this.config.seedContext, this.logger);
|
|
595
|
+
const hasSeed = seedTurns.length > 0;
|
|
596
|
+
// Map slide ids → short human labels for the prompt. Anything not in
|
|
597
|
+
// the map is dropped (defence against stale ids leaking through).
|
|
598
|
+
const SLIDE_LABEL = {
|
|
599
|
+
title: 'the Hailer overview cover',
|
|
600
|
+
what: 'what Hailer is in one line',
|
|
601
|
+
problem: 'the trackers-vs-chat problem framing',
|
|
602
|
+
'core-label': 'the core-concepts section',
|
|
603
|
+
workspaces: 'Workspaces',
|
|
604
|
+
workflows: 'Workflows',
|
|
605
|
+
activities: 'Activities',
|
|
606
|
+
discussions: 'Discussions',
|
|
607
|
+
'build-label': 'the build-and-extend section',
|
|
608
|
+
'apps-studio': 'Apps & Hailer Studio',
|
|
609
|
+
sdk: 'the Hailer SDK',
|
|
610
|
+
'sdk-code': 'the workflow-as-code example',
|
|
611
|
+
mcp: 'Hailer MCP',
|
|
612
|
+
insights: 'Insights & Automations',
|
|
613
|
+
closer: 'the closing one-platform statement',
|
|
614
|
+
};
|
|
615
|
+
const slideLabels = viewedSlides
|
|
616
|
+
.map((id) => SLIDE_LABEL[id])
|
|
617
|
+
.filter((s) => typeof s === 'string');
|
|
618
|
+
const slideHint = slideLabels.length > 0
|
|
619
|
+
? `\n\nAdditional signal: during the demo the visitor explicitly opened these overview slides — ${slideLabels.join(', ')}. Treat that as an interest signal. Weight your concrete suggestions toward those concepts when the demo conversation is ambiguous, but don't read more into it than "they paid attention here."`
|
|
620
|
+
: '';
|
|
621
|
+
// Only promise an automatic build when Studio session creation is
|
|
622
|
+
// configured for this deployment; otherwise nothing builds the
|
|
623
|
+
// workspace and the bot must not promise a build that never runs. The
|
|
624
|
+
// graduation flow gates createStudioSession on the same env var, so
|
|
625
|
+
// "build promised" matches "build attempted".
|
|
626
|
+
const studioBuilding = !!config_1.environment.STUDIO_SESSIONS_CREATE_URL;
|
|
627
|
+
const introInstruction = hasSeed
|
|
628
|
+
? (studioBuilding
|
|
629
|
+
? `You are being posted in your own activity discussion for the first time. The user just graduated from a public demo chat with you — the messages above are the conversation you both had there.
|
|
630
|
+
|
|
631
|
+
What this first message needs to do:
|
|
632
|
+
|
|
633
|
+
1. Greet them warmly and acknowledge you remember the demo. Be specific — name the thing they were exploring (e.g. "the plumbing-company job tracker you were sketching out" rather than "your business").
|
|
634
|
+
|
|
635
|
+
2. Tell them what's happening RIGHT NOW: based on that conversation, their workspace is being set up for them automatically — the workflow(s) to run that process, a few sample records so they can see it in action, and a custom app on top. It takes a couple of minutes. Do NOT offer to build it yourself and do NOT ask "want me to set this up?" — it is already being built. You are not the one building it; just let them know it's underway.
|
|
636
|
+
|
|
637
|
+
3. Tell them what comes next: as soon as the workflows are ready you'll walk them through their new workspace with a quick guided tour, so there's nothing they need to do yet — they can sit tight or keep chatting with you here.
|
|
638
|
+
|
|
639
|
+
4. Mention in passing that they can shape how you behave (tone, persona, language, even your picture) just by asking you here, and that account-level settings live in the AI Hub app.
|
|
640
|
+
|
|
641
|
+
Tone: conversational, friendly, reassuring. No section headers, no bullet lists. Keep it under ~150 words.`
|
|
642
|
+
: `You are being posted in your own activity discussion for the first time. The user just graduated from a public demo chat with you — the messages above are the conversation you both had there. NOTE: there is no automatic workspace builder running in this deployment, so YOU are how they get set up.
|
|
643
|
+
|
|
644
|
+
What this first message needs to do:
|
|
645
|
+
|
|
646
|
+
1. Greet them warmly and acknowledge you remember the demo. Be specific — name the thing they were exploring (e.g. "the plumbing-company job tracker you were sketching out" rather than "your business").
|
|
647
|
+
|
|
648
|
+
2. Scan the demo for concrete setup intent — a process with stages, or a list of records they want to manage. Offer to set it up for them now: be concrete, naming the phases you'd create (e.g. Quoted → Scheduled → In Progress → Invoiced → Paid) for a lifecycle, or describing it as a simple list for reference data. End with a clear yes/no like "Want me to set this up?" — don't start building unilaterally.
|
|
649
|
+
|
|
650
|
+
3. If the demo is too vague to start, ask ONE or TWO concrete clarifying questions instead.
|
|
651
|
+
|
|
652
|
+
4. Mention in passing that they can shape how you behave (tone, persona, language, even your picture) just by asking you here, and that account-level settings live in the AI Hub app.
|
|
653
|
+
|
|
654
|
+
Tone: conversational, friendly, action-oriented. No section headers; short bullets are fine only when naming proposed phases or fields. Keep it under ~180 words.`)
|
|
655
|
+
: `You are being posted in your own activity discussion for the first time. Introduce yourself briefly in your own voice.
|
|
656
|
+
|
|
657
|
+
CRITICAL: do NOT invent a backstory or claim to be a real person, celebrity, or fictional character based on your name alone. Your name was just chosen by the user — they may have meant a specific person/character, or they may have just liked the sound of it. If you don't have explicit persona instructions, acknowledge your name but be honest that you're a fresh assistant without a defined persona yet, and ask what kind of assistant they want you to be (or invite them to configure you).
|
|
658
|
+
|
|
659
|
+
In all cases, let the user know they can change how you behave — your tone, persona, language, dos and donts, even your profile picture — just by asking you here in chat. They can also ask to see your current configuration. For account-level settings (credentials, who can talk to you, etc.) point them at the AI Hub app.
|
|
660
|
+
|
|
661
|
+
Tell them how to actually get your attention: describe your current response mode in one sentence (you can see it in <your-settings>). For example, if you only respond to mentions or replies, say so — otherwise they'll wonder why you're silent in shared discussions. Mention that they can switch you to a different mode in AI Hub if it doesn't suit them.
|
|
662
|
+
|
|
663
|
+
Keep it short and conversational. No bullet lists, no headers.`;
|
|
664
|
+
const introInstructionFinal = introInstruction + slideHint;
|
|
665
|
+
const messages = [
|
|
666
|
+
...seedTurns,
|
|
667
|
+
{ role: 'user', content: introInstructionFinal },
|
|
668
|
+
];
|
|
669
|
+
let response;
|
|
670
|
+
try {
|
|
671
|
+
response = await this.anthropic.messages.create({
|
|
672
|
+
model: MODEL_HAIKU,
|
|
673
|
+
max_tokens: 400,
|
|
674
|
+
temperature: 0.7,
|
|
675
|
+
system: systemPrompt,
|
|
676
|
+
messages,
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
catch (err) {
|
|
680
|
+
this.logger.warn('Self-intro: LLM call failed', {
|
|
681
|
+
activityId: this.config.activityId,
|
|
682
|
+
error: err instanceof Error ? err.message : String(err),
|
|
683
|
+
});
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
const text = response.content
|
|
687
|
+
.filter((b) => b.type === 'text')
|
|
688
|
+
.map((b) => b.text)
|
|
689
|
+
.join('')
|
|
690
|
+
.trim();
|
|
691
|
+
if (!text) {
|
|
692
|
+
this.logger.warn('Self-intro: empty LLM response', { activityId: this.config.activityId });
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
// Bill the intro generation to the workspace — same as any other LLM call.
|
|
696
|
+
if (this.tokenBilling && this._workspaceId) {
|
|
697
|
+
const usage = response.usage;
|
|
698
|
+
const cacheCreation = usage.cache_creation_input_tokens || 0;
|
|
699
|
+
const cacheRead = usage.cache_read_input_tokens || 0;
|
|
700
|
+
const cost = this.tokenBilling.calculateCost(usage.input_tokens, usage.output_tokens, cacheCreation, cacheRead, MODEL_HAIKU);
|
|
701
|
+
this.tokenBilling.burnTokens({
|
|
702
|
+
workspaceId: this._workspaceId,
|
|
703
|
+
inputTokens: usage.input_tokens, outputTokens: usage.output_tokens,
|
|
704
|
+
cacheCreationTokens: cacheCreation, cacheReadTokens: cacheRead, costUsd: cost,
|
|
705
|
+
sessionId: discussionId, model: MODEL_HAIKU,
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
try {
|
|
709
|
+
await this.client.socket.request(hailer_rpc_1.HailerRpc.Messenger.send, [{ msg: text }, discussionId]);
|
|
710
|
+
}
|
|
711
|
+
catch (err) {
|
|
712
|
+
const { msg: errMsg, code } = (0, bot_config_1.extractHailerError)(err);
|
|
713
|
+
this.logger.warn('Self-intro: messenger.send failed', {
|
|
714
|
+
activityId: this.config.activityId,
|
|
715
|
+
discussionId,
|
|
716
|
+
msg: errMsg,
|
|
717
|
+
code,
|
|
718
|
+
});
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
entry.introPosted = true;
|
|
722
|
+
(0, bot_config_1.saveWorkspaceConfigFile)(cfg);
|
|
723
|
+
(0, bot_config_1.invalidateConfigCache)();
|
|
724
|
+
this.logger.info('Posted self-intro in own discussion', {
|
|
725
|
+
activityId: this.config.activityId,
|
|
726
|
+
discussionId,
|
|
727
|
+
length: text.length,
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Teardown: stop the bot's websocket and live state. We intentionally do
|
|
732
|
+
* NOT delete the Hailer user account from here — that was previously a
|
|
733
|
+
* source of mass user-account deletion when the gone-classifier fired on
|
|
734
|
+
* transient permission errors. Orphaned bot users are reaped out-of-band
|
|
735
|
+
* by Hailer's inactive-user prune.
|
|
736
|
+
*/
|
|
737
|
+
async destroy() {
|
|
738
|
+
await this.stop();
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Remove this bot's entry from .bot-config/{workspaceId}.json. Called when
|
|
742
|
+
* the bot's own activity has been deleted in Hailer (detected via the
|
|
743
|
+
* self-probe in start()). Idempotent — safe if the entry is already gone.
|
|
744
|
+
*/
|
|
745
|
+
pruneSelfFromConfig() {
|
|
746
|
+
if (!this.config.activityId || !this.config.workspaceId) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
try {
|
|
750
|
+
const cfg = (0, bot_config_1.loadWorkspaceConfigFile)(this.config.workspaceId);
|
|
751
|
+
const before = cfg.bots.length;
|
|
752
|
+
cfg.bots = cfg.bots.filter((b) => b.activityId !== this.config.activityId);
|
|
753
|
+
if (cfg.bots.length === before) {
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
(0, bot_config_1.saveWorkspaceConfigFile)(cfg);
|
|
757
|
+
(0, bot_config_1.invalidateConfigCache)();
|
|
758
|
+
this.logger.info('Pruned self from bot config', {
|
|
759
|
+
activityId: this.config.activityId,
|
|
760
|
+
workspaceId: this.config.workspaceId,
|
|
761
|
+
remaining: cfg.bots.length,
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
catch (err) {
|
|
765
|
+
this.logger.warn('Failed to prune self from bot config', {
|
|
766
|
+
activityId: this.config.activityId,
|
|
767
|
+
error: err instanceof Error ? err.message : err,
|
|
768
|
+
});
|
|
769
|
+
}
|
|
222
770
|
}
|
|
223
771
|
async stop() {
|
|
224
772
|
this.logger.debug('Stopping bot');
|
|
@@ -247,8 +795,9 @@ class Bot {
|
|
|
247
795
|
}
|
|
248
796
|
// ===== SIGNAL HANDLING =====
|
|
249
797
|
async handleSignal(signal) {
|
|
250
|
-
if (!this._connected || !this.messageClassifier)
|
|
798
|
+
if (!this._connected || !this.messageClassifier) {
|
|
251
799
|
return;
|
|
800
|
+
}
|
|
252
801
|
try {
|
|
253
802
|
const rawMsgId = signal.data.msg_id;
|
|
254
803
|
const discussionId = signal.data.discussion;
|
|
@@ -257,23 +806,27 @@ class Bot {
|
|
|
257
806
|
this.logger.warn('messenger.new signal has no dedup key');
|
|
258
807
|
return;
|
|
259
808
|
}
|
|
260
|
-
if (this.signalRouter.isDuplicate(dedupKey))
|
|
809
|
+
if (this.signalRouter.isDuplicate(dedupKey)) {
|
|
261
810
|
return;
|
|
811
|
+
}
|
|
262
812
|
const message = await this.messageClassifier.extractIncomingMessage(signal);
|
|
263
813
|
if (!message) {
|
|
264
|
-
if (rawMsgId)
|
|
814
|
+
if (rawMsgId) {
|
|
265
815
|
this.signalRouter.removeDedupKey(rawMsgId);
|
|
816
|
+
}
|
|
266
817
|
return;
|
|
267
818
|
}
|
|
268
819
|
// Self-message guard — always skip own messages
|
|
269
|
-
if (message.senderId === this.userId)
|
|
820
|
+
if (message.senderId === this.userId) {
|
|
270
821
|
return;
|
|
822
|
+
}
|
|
271
823
|
// Bot-to-bot guard
|
|
272
824
|
if (this.botManager && this._workspaceId) {
|
|
273
825
|
const botUserIds = this.botManager.getBotUserIdsForWorkspace(this._workspaceId);
|
|
274
826
|
if (botUserIds.has(message.senderId)) {
|
|
275
|
-
if (!this.config.allowBotMessages)
|
|
276
|
-
return;
|
|
827
|
+
if (!this.config.allowBotMessages) {
|
|
828
|
+
return;
|
|
829
|
+
} // Hard block if not opted in
|
|
277
830
|
// Only respond to bot messages if explicitly mentioned or replied to
|
|
278
831
|
if (!message.isMention && !message.isReplyToBot) {
|
|
279
832
|
this.logger.debug('Ignoring bot message - no mention or reply', {
|
|
@@ -304,27 +857,31 @@ class Bot {
|
|
|
304
857
|
}
|
|
305
858
|
}
|
|
306
859
|
// Rate limiting
|
|
307
|
-
if (this.signalRouter.isRateLimited(message.discussionId))
|
|
860
|
+
if (this.signalRouter.isRateLimited(message.discussionId)) {
|
|
308
861
|
return;
|
|
862
|
+
}
|
|
309
863
|
// Layer 1: Workspace membership
|
|
310
864
|
if (!this.permissions.isMember(message.senderId)) {
|
|
311
865
|
this.logger.debug('Ignoring message - not a workspace member', { senderId: message.senderId });
|
|
312
|
-
if (rawMsgId)
|
|
866
|
+
if (rawMsgId) {
|
|
313
867
|
this.signalRouter.removeDedupKey(rawMsgId);
|
|
868
|
+
}
|
|
314
869
|
return;
|
|
315
870
|
}
|
|
316
871
|
// Layer 2: Access level
|
|
317
872
|
if (!this.permissions.meetsAccessLevel(message.senderId, this.config.accessLevel)) {
|
|
318
873
|
this.logger.debug('Ignoring message - access level not met', { senderId: message.senderId });
|
|
319
|
-
if (rawMsgId)
|
|
874
|
+
if (rawMsgId) {
|
|
320
875
|
this.signalRouter.removeDedupKey(rawMsgId);
|
|
876
|
+
}
|
|
321
877
|
return;
|
|
322
878
|
}
|
|
323
879
|
// Engagement logic
|
|
324
880
|
const state = this.signalRouter.getOrCreateState(message.discussionId);
|
|
325
881
|
state.contextBuffer.push(message);
|
|
326
|
-
if (!this.signalRouter.checkTrigger(message, this._responseMode))
|
|
882
|
+
if (!this.signalRouter.checkTrigger(message, this._responseMode)) {
|
|
327
883
|
return;
|
|
884
|
+
}
|
|
328
885
|
const isExplicit = this.signalRouter.isExplicitTrigger(message);
|
|
329
886
|
if (state.state === 'idle') {
|
|
330
887
|
if (isExplicit || this._responseMode === 'always') {
|
|
@@ -336,8 +893,9 @@ class Bot {
|
|
|
336
893
|
return;
|
|
337
894
|
}
|
|
338
895
|
}
|
|
339
|
-
if (this._responseMode !== 'always' && !isExplicit)
|
|
896
|
+
if (this._responseMode !== 'always' && !isExplicit) {
|
|
340
897
|
return;
|
|
898
|
+
}
|
|
341
899
|
// Process if not already running
|
|
342
900
|
if (!this.signalRouter.processingDiscussions.has(message.discussionId)) {
|
|
343
901
|
this.processDiscussion(message.discussionId).catch(err => {
|
|
@@ -352,17 +910,20 @@ class Bot {
|
|
|
352
910
|
}
|
|
353
911
|
// ===== DISCUSSION PROCESSING =====
|
|
354
912
|
async processDiscussion(discussionId) {
|
|
355
|
-
if (this.signalRouter.processingDiscussions.has(discussionId))
|
|
913
|
+
if (this.signalRouter.processingDiscussions.has(discussionId)) {
|
|
356
914
|
return;
|
|
915
|
+
}
|
|
357
916
|
this.signalRouter.processingDiscussions.add(discussionId);
|
|
358
917
|
const state = this.signalRouter.getOrCreateState(discussionId);
|
|
359
918
|
try {
|
|
360
919
|
while (true) {
|
|
361
920
|
const messages = state.contextBuffer.splice(0);
|
|
362
|
-
if (messages.length === 0)
|
|
921
|
+
if (messages.length === 0) {
|
|
363
922
|
break;
|
|
364
|
-
|
|
923
|
+
}
|
|
924
|
+
if (messages.length > 1) {
|
|
365
925
|
this.opLogger.coalesce(discussionId, messages.length);
|
|
926
|
+
}
|
|
366
927
|
// Build conversation entry
|
|
367
928
|
const conversation = this.conversationManager.getConversation(discussionId);
|
|
368
929
|
const parts = messages.map(msg => this.formatIncomingMessage(msg));
|
|
@@ -380,8 +941,9 @@ class Bot {
|
|
|
380
941
|
const primaryMessage = messages[messages.length - 1];
|
|
381
942
|
state.lastProgressTime = 0;
|
|
382
943
|
state.abortController = new AbortController();
|
|
383
|
-
for (const msg of messages)
|
|
944
|
+
for (const msg of messages) {
|
|
384
945
|
this.opLogger.messageIn(msg.discussionId, msg.senderName, msg.content);
|
|
946
|
+
}
|
|
385
947
|
let responded;
|
|
386
948
|
try {
|
|
387
949
|
responded = await this.processMessage(primaryMessage, state.abortController.signal);
|
|
@@ -404,12 +966,14 @@ class Bot {
|
|
|
404
966
|
break;
|
|
405
967
|
}
|
|
406
968
|
}
|
|
407
|
-
if (state.contextBuffer.length === 0)
|
|
969
|
+
if (state.contextBuffer.length === 0) {
|
|
408
970
|
break;
|
|
971
|
+
}
|
|
409
972
|
if (state.state === 'idle') {
|
|
410
973
|
const hasExplicit = state.contextBuffer.some(m => this.signalRouter.isExplicitTrigger(m));
|
|
411
|
-
if (!hasExplicit)
|
|
974
|
+
if (!hasExplicit) {
|
|
412
975
|
break;
|
|
976
|
+
}
|
|
413
977
|
state.state = 'engaged';
|
|
414
978
|
state.consecutiveNonResponses = 0;
|
|
415
979
|
this.opLogger.engage(discussionId, 'reengaged');
|
|
@@ -443,8 +1007,9 @@ class Bot {
|
|
|
443
1007
|
return await this.runLlmLoop(message, route, signal);
|
|
444
1008
|
}
|
|
445
1009
|
catch (error) {
|
|
446
|
-
if (error instanceof sdk_1.default.APIUserAbortError)
|
|
1010
|
+
if (error instanceof sdk_1.default.APIUserAbortError) {
|
|
447
1011
|
throw error;
|
|
1012
|
+
}
|
|
448
1013
|
this.logger.error('Message processing failed', error);
|
|
449
1014
|
this.typingIndicator?.stop(message.discussionId);
|
|
450
1015
|
conversation.length = snapshotLength;
|
|
@@ -454,6 +1019,14 @@ class Bot {
|
|
|
454
1019
|
// ===== MODEL ROUTING =====
|
|
455
1020
|
async routeMessage(message, conversation, signal) {
|
|
456
1021
|
const defaultRoute = { model: MODEL_HAIKU, maxTokens: 2000 };
|
|
1022
|
+
// Any file attachment forces COMPLEX: reading/analysing an attachment plus the
|
|
1023
|
+
// tool calls that typically follow easily blows Haiku's 2000-token budget,
|
|
1024
|
+
// and a truncated response with an unfinished tool_use poisons the conversation.
|
|
1025
|
+
if (message.fileAttachments && message.fileAttachments.length > 0) {
|
|
1026
|
+
const route = { ...SONNET_ROUTE };
|
|
1027
|
+
this.opLogger.route(message.discussionId, 'COMPLEX (attachments)', route.model, message.content);
|
|
1028
|
+
return route;
|
|
1029
|
+
}
|
|
457
1030
|
try {
|
|
458
1031
|
const wsName = this.init?.network?.name || 'Workspace';
|
|
459
1032
|
const recentContext = this.getRecentContext(conversation);
|
|
@@ -465,7 +1038,7 @@ class Bot {
|
|
|
465
1038
|
this.trackTokenUsage(response, message, MODEL_HAIKU);
|
|
466
1039
|
const text = response.content.filter((b) => b.type === 'text').map(b => b.text).join('').trim().toUpperCase();
|
|
467
1040
|
if (text.includes('COMPLEX')) {
|
|
468
|
-
const route = {
|
|
1041
|
+
const route = { ...SONNET_ROUTE };
|
|
469
1042
|
this.opLogger.route(message.discussionId, 'COMPLEX', route.model, message.content);
|
|
470
1043
|
return route;
|
|
471
1044
|
}
|
|
@@ -488,8 +1061,9 @@ class Bot {
|
|
|
488
1061
|
else if (Array.isArray(msg.content)) {
|
|
489
1062
|
text = msg.content.filter((b) => b.type === 'text').map((b) => b.text).join(' ');
|
|
490
1063
|
}
|
|
491
|
-
if (!text)
|
|
1064
|
+
if (!text) {
|
|
492
1065
|
continue;
|
|
1066
|
+
}
|
|
493
1067
|
lines.unshift(`${msg.role === 'user' ? 'User' : 'Assistant'}: ${text.length > 200 ? text.slice(0, 200) + '...' : text}`);
|
|
494
1068
|
count++;
|
|
495
1069
|
}
|
|
@@ -505,38 +1079,54 @@ class Bot {
|
|
|
505
1079
|
const systemPrompt = (0, services_1.buildSystemPrompt)({
|
|
506
1080
|
init: this.init, userId: this.userId,
|
|
507
1081
|
workspaceOverview: this.workspaceOverview, customPrompt: this._systemPrompt,
|
|
508
|
-
botExchangeContext,
|
|
1082
|
+
botExchangeContext, helperMode: this.config.helperMode,
|
|
1083
|
+
hasSeedContext: !!this.config.seedContext,
|
|
1084
|
+
responseMode: this._responseMode,
|
|
1085
|
+
accessLevel: this.config.accessLevel,
|
|
1086
|
+
allowBotMessages: this.config.allowBotMessages,
|
|
509
1087
|
});
|
|
510
1088
|
const tools = this.cachedTools || this.getAnthropicTools();
|
|
511
1089
|
for (let i = 0;; i++) {
|
|
512
|
-
const cachedConversation = this.conversationManager.prepareForCaching(conversation);
|
|
513
1090
|
this.typingIndicator?.updateStatus(message.discussionId, i === 0 ? 'Thinking' : 'Processing');
|
|
514
1091
|
let response;
|
|
515
1092
|
let llmStart = Date.now();
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
messages: cachedConversation, tools,
|
|
521
|
-
}, { signal });
|
|
522
|
-
}
|
|
523
|
-
catch (error) {
|
|
524
|
-
if (error instanceof sdk_1.default.APIUserAbortError) {
|
|
525
|
-
this.cleanupIncompleteExchange(conversation);
|
|
526
|
-
this.opLogger.interrupt(message.discussionId, 'new message received');
|
|
527
|
-
return false;
|
|
528
|
-
}
|
|
529
|
-
if (route.model !== MODEL_HAIKU) {
|
|
530
|
-
this.logger.warn('Sonnet failed, falling back to Haiku');
|
|
531
|
-
route = { model: MODEL_HAIKU, maxTokens: 2000 };
|
|
532
|
-
llmStart = Date.now();
|
|
1093
|
+
let trimAttempts = 0;
|
|
1094
|
+
while (true) {
|
|
1095
|
+
try {
|
|
1096
|
+
const cachedConversation = this.conversationManager.prepareForCaching(conversation);
|
|
533
1097
|
response = await this.anthropic.messages.create({
|
|
534
|
-
model:
|
|
1098
|
+
model: route.model, max_tokens: route.maxTokens, temperature: 0,
|
|
535
1099
|
system: [{ type: 'text', text: systemPrompt, cache_control: { type: 'ephemeral' } }],
|
|
536
1100
|
messages: cachedConversation, tools,
|
|
537
1101
|
}, { signal });
|
|
1102
|
+
break;
|
|
538
1103
|
}
|
|
539
|
-
|
|
1104
|
+
catch (error) {
|
|
1105
|
+
if (error instanceof sdk_1.default.APIUserAbortError) {
|
|
1106
|
+
this.cleanupIncompleteExchange(conversation);
|
|
1107
|
+
this.opLogger.interrupt(message.discussionId, 'new message received');
|
|
1108
|
+
return false;
|
|
1109
|
+
}
|
|
1110
|
+
const isPromptTooLong = error instanceof sdk_1.default.BadRequestError &&
|
|
1111
|
+
/prompt is too long/i.test(error.message || '');
|
|
1112
|
+
if (isPromptTooLong && trimAttempts < MAX_PROMPT_TRIM_ATTEMPTS && conversation.length >= 4) {
|
|
1113
|
+
trimAttempts++;
|
|
1114
|
+
const dropped = conversation.splice(0, 2);
|
|
1115
|
+
this.logger.warn('Prompt too long — dropped oldest conversation pair', {
|
|
1116
|
+
attempt: trimAttempts,
|
|
1117
|
+
droppedRoles: dropped.map(msg => msg.role),
|
|
1118
|
+
remainingMessages: conversation.length,
|
|
1119
|
+
model: route.model,
|
|
1120
|
+
});
|
|
1121
|
+
llmStart = Date.now();
|
|
1122
|
+
continue;
|
|
1123
|
+
}
|
|
1124
|
+
if (route.model !== MODEL_HAIKU && !isPromptTooLong) {
|
|
1125
|
+
this.logger.warn('Sonnet failed, falling back to Haiku');
|
|
1126
|
+
route = { model: MODEL_HAIKU, maxTokens: 2000 };
|
|
1127
|
+
llmStart = Date.now();
|
|
1128
|
+
continue;
|
|
1129
|
+
}
|
|
540
1130
|
throw error;
|
|
541
1131
|
}
|
|
542
1132
|
}
|
|
@@ -546,12 +1136,37 @@ class Bot {
|
|
|
546
1136
|
const cacheRead = usage.cache_read_input_tokens || 0;
|
|
547
1137
|
const totalInput = usage.input_tokens + cacheRead + (usage.cache_creation_input_tokens || 0);
|
|
548
1138
|
this.opLogger.llmCall(message.discussionId, route.model, usage.input_tokens, usage.output_tokens, totalInput > 0 ? Math.round((cacheRead / totalInput) * 100) : 0, llmDuration);
|
|
549
|
-
if (!response.content || response.content.length === 0)
|
|
1139
|
+
if (!response.content || response.content.length === 0) {
|
|
550
1140
|
break;
|
|
1141
|
+
}
|
|
551
1142
|
conversation.push({ role: 'assistant', content: response.content });
|
|
552
|
-
// Truncation guard
|
|
1143
|
+
// Truncation guard. When the response is cut off at max_tokens
|
|
1144
|
+
// AND the truncated assistant content contains tool_use blocks,
|
|
1145
|
+
// Anthropic's API requires the very next message to carry
|
|
1146
|
+
// matching tool_result blocks — otherwise every subsequent
|
|
1147
|
+
// request fails with `tool_use ids were found without tool_result
|
|
1148
|
+
// blocks immediately after`. We can't actually execute the
|
|
1149
|
+
// truncated tools (JSON inputs may be malformed, and the model
|
|
1150
|
+
// expected all of them to run together), so we synthesize error
|
|
1151
|
+
// tool_results that tell the model exactly why nothing ran.
|
|
553
1152
|
if (response.stop_reason === 'max_tokens') {
|
|
554
|
-
|
|
1153
|
+
const truncatedToolUses = response.content.filter((b) => b.type === 'tool_use');
|
|
1154
|
+
const reminder = 'Your previous response was truncated because it exceeded the output token limit. Break it into smaller batches of 25-50 items per tool call.';
|
|
1155
|
+
if (truncatedToolUses.length > 0) {
|
|
1156
|
+
const errorResults = truncatedToolUses.map(b => ({
|
|
1157
|
+
type: 'tool_result',
|
|
1158
|
+
tool_use_id: b.id,
|
|
1159
|
+
content: 'Tool call was truncated before it could run because the response exceeded the output token limit. Try again with a smaller batch.',
|
|
1160
|
+
is_error: true,
|
|
1161
|
+
}));
|
|
1162
|
+
conversation.push({
|
|
1163
|
+
role: 'user',
|
|
1164
|
+
content: [...errorResults, { type: 'text', text: reminder }],
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
else {
|
|
1168
|
+
conversation.push({ role: 'user', content: reminder });
|
|
1169
|
+
}
|
|
555
1170
|
continue;
|
|
556
1171
|
}
|
|
557
1172
|
// Tool calls
|
|
@@ -565,7 +1180,7 @@ class Bot {
|
|
|
565
1180
|
if (route.model === MODEL_HAIKU) {
|
|
566
1181
|
const failedCount = toolResults.filter(r => r.is_error).length;
|
|
567
1182
|
if (failedCount >= 2) {
|
|
568
|
-
route = {
|
|
1183
|
+
route = { ...SONNET_ROUTE };
|
|
569
1184
|
await this.sendMessage(message.discussionId, 'Switching to a more capable model to handle this.');
|
|
570
1185
|
}
|
|
571
1186
|
}
|
|
@@ -595,10 +1210,30 @@ class Bot {
|
|
|
595
1210
|
for (const block of toolUseBlocks) {
|
|
596
1211
|
const toolStart = Date.now();
|
|
597
1212
|
const args = block.input;
|
|
598
|
-
//
|
|
599
|
-
|
|
1213
|
+
// Bot profile gate — the model is only offered BOT_TOOLS, but enforce
|
|
1214
|
+
// at execution too so a hallucinated tool name can't reach the registry.
|
|
1215
|
+
// Resolve deprecation aliases first so mid-conversation calls under
|
|
1216
|
+
// old v1 names keep working during the grace release.
|
|
1217
|
+
if (!tool_profiles_1.BOT_TOOLS.has(this.toolExecutor.resolveName(block.name))) {
|
|
1218
|
+
this.opLogger.permDenied(message.discussionId, block.name, 'n/a', 'not-in-bot-tools');
|
|
1219
|
+
results.push({
|
|
1220
|
+
type: 'tool_result',
|
|
1221
|
+
tool_use_id: block.id,
|
|
1222
|
+
content: `Tool not available: ${block.name} is not part of this bot's toolset. `
|
|
1223
|
+
+ 'Use only the tools you were given — do not retry.',
|
|
1224
|
+
is_error: true,
|
|
1225
|
+
});
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
1228
|
+
// Admin-only gate (args-aware: merged tools gate per-action)
|
|
1229
|
+
if (this.permissions.isAdminOnlyTool(block.name, args) && !this.permissions.isAdminOrOwner(message.senderId)) {
|
|
600
1230
|
this.opLogger.permDenied(message.discussionId, block.name, 'n/a', 'requires-admin');
|
|
601
|
-
results.push({
|
|
1231
|
+
results.push({
|
|
1232
|
+
type: 'tool_result',
|
|
1233
|
+
tool_use_id: block.id,
|
|
1234
|
+
content: `Permission denied: ${block.name} requires workspace admin or owner permission. Explain to the user that this action is restricted to admins/owners — do not retry, do not try a workaround, do not pretend it succeeded.`,
|
|
1235
|
+
is_error: true,
|
|
1236
|
+
});
|
|
602
1237
|
continue;
|
|
603
1238
|
}
|
|
604
1239
|
// Pre-execution permission check
|
|
@@ -613,7 +1248,7 @@ class Bot {
|
|
|
613
1248
|
// Discussion isolation: prevent leaking DMs and discussions the user shouldn't access
|
|
614
1249
|
if (DISCUSSION_TOOLS.has(block.name) && args.discussionId && args.discussionId !== message.discussionId) {
|
|
615
1250
|
try {
|
|
616
|
-
const discResult = await this.client.socket.request(
|
|
1251
|
+
const discResult = await this.client.socket.request(hailer_rpc_1.HailerRpc.Discussion.messageLatest, [args.discussionId]);
|
|
617
1252
|
const disc = discResult?.discussion || {};
|
|
618
1253
|
const participants = disc.participants || [];
|
|
619
1254
|
const isPrivate = disc.private === true;
|
|
@@ -648,10 +1283,12 @@ class Bot {
|
|
|
648
1283
|
// Auto-inject for join_discussion
|
|
649
1284
|
if (block.name === 'join_discussion') {
|
|
650
1285
|
toolArgs = { ...toolArgs };
|
|
651
|
-
if (!toolArgs.inviteUserId && message.senderId)
|
|
1286
|
+
if (!toolArgs.inviteUserId && message.senderId) {
|
|
652
1287
|
toolArgs.inviteUserId = message.senderId;
|
|
653
|
-
|
|
1288
|
+
}
|
|
1289
|
+
if (!toolArgs.welcomeReason && message.content) {
|
|
654
1290
|
toolArgs.welcomeReason = message.content;
|
|
1291
|
+
}
|
|
655
1292
|
// Only auto-inject sourceActivityId if explicitly provided — don't link DMs to random activities
|
|
656
1293
|
}
|
|
657
1294
|
const result = await this.toolExecutor.execute(block.name, toolArgs, this.getUserContext());
|
|
@@ -685,6 +1322,11 @@ class Bot {
|
|
|
685
1322
|
if (signal?.aborted && WRITE_TOOLS.has(block.name)) {
|
|
686
1323
|
this.logger.warn('Write tool completed before abort', { tool: block.name });
|
|
687
1324
|
}
|
|
1325
|
+
if (contentStr.length > MAX_TOOL_RESULT_CHARS) {
|
|
1326
|
+
const originalLen = contentStr.length;
|
|
1327
|
+
contentStr = `${contentStr.slice(0, MAX_TOOL_RESULT_CHARS)}\n\n[Tool result truncated. Original: ${originalLen} chars (~${Math.round(originalLen / 4)} tokens). Narrow your query: use 'fields' parameter, add filters, lower 'limit', or use preview_insight for aggregates.]`;
|
|
1328
|
+
this.logger.warn('Tool result truncated', { tool: block.name, originalLen, truncatedTo: MAX_TOOL_RESULT_CHARS });
|
|
1329
|
+
}
|
|
688
1330
|
results.push({ type: 'tool_result', tool_use_id: block.id, content: contentStr });
|
|
689
1331
|
}
|
|
690
1332
|
catch (error) {
|
|
@@ -708,43 +1350,47 @@ class Bot {
|
|
|
708
1350
|
const parts = [];
|
|
709
1351
|
const unsupported = [];
|
|
710
1352
|
for (const f of message.fileAttachments) {
|
|
711
|
-
if (!f.mime || (f.size && f.size >
|
|
1353
|
+
if (!f.mime || (f.size && f.size > fileSizeLimit(f.mime))) {
|
|
712
1354
|
unsupported.push(f.fileId);
|
|
1355
|
+
continue;
|
|
713
1356
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
}
|
|
717
|
-
else if (DOCUMENT_MIME_TYPES.has(f.mime) || TEXT_MIME_TYPES.has(f.mime)) {
|
|
718
|
-
parts.push(`[Document attached: ${f.filename} — included below]`);
|
|
719
|
-
}
|
|
720
|
-
else {
|
|
1357
|
+
const desc = describeAttachment(f.mime, f.filename, f.fileId);
|
|
1358
|
+
if (!desc) {
|
|
721
1359
|
unsupported.push(f.fileId);
|
|
1360
|
+
continue;
|
|
722
1361
|
}
|
|
1362
|
+
parts.push(desc);
|
|
723
1363
|
}
|
|
724
|
-
if (unsupported.length)
|
|
1364
|
+
if (unsupported.length) {
|
|
725
1365
|
parts.push(`[File attached: ${unsupported.length} file(s) - IDs: ${unsupported.join(', ')}]\nUse download_file tool with fileId to read file contents.`);
|
|
726
|
-
|
|
1366
|
+
}
|
|
1367
|
+
if (parts.length) {
|
|
727
1368
|
fileInfo = '\n' + parts.join('\n');
|
|
1369
|
+
}
|
|
728
1370
|
}
|
|
729
1371
|
const dmNote = isDm ? '\n[This is a private DM conversation, not an activity discussion.]' : '';
|
|
730
|
-
|
|
1372
|
+
const aiContext = message.meta?.aiContext ? `<context>${JSON.stringify(message.meta.aiContext)}</context>\n\n` : '';
|
|
1373
|
+
return `<incoming discussion="${message.discussionId}"${typeAttr}${nameAttr}${activityAttr} from="${message.senderName}" user_id="${message.senderId}" timestamp="${new Date(message.timestamp).toISOString()}">\n${aiContext}${message.content}${fileInfo}${dmNote}\n</incoming>`;
|
|
731
1374
|
}
|
|
732
1375
|
async resolveFileAttachments(messages) {
|
|
733
|
-
if (!this.hailerApi)
|
|
1376
|
+
if (!this.hailerApi) {
|
|
734
1377
|
return [];
|
|
735
|
-
|
|
1378
|
+
}
|
|
1379
|
+
const nonOffice = [];
|
|
1380
|
+
const office = [];
|
|
1381
|
+
const skipped = [];
|
|
736
1382
|
for (const msg of messages) {
|
|
737
1383
|
for (const f of msg.fileAttachments || []) {
|
|
738
|
-
if (!f.mime || (f.size && f.size >
|
|
1384
|
+
if (!f.mime || (f.size && f.size > fileSizeLimit(f.mime))) {
|
|
1385
|
+
continue;
|
|
1386
|
+
}
|
|
1387
|
+
if (!isSupportedMime(f.mime)) {
|
|
739
1388
|
continue;
|
|
740
|
-
if (IMAGE_MIME_TYPES.has(f.mime) || DOCUMENT_MIME_TYPES.has(f.mime) || TEXT_MIME_TYPES.has(f.mime)) {
|
|
741
|
-
eligible.push({ fileId: f.fileId, mime: f.mime });
|
|
742
1389
|
}
|
|
1390
|
+
(isOfficeMime(f.mime) ? office : nonOffice).push({ fileId: f.fileId, mime: f.mime, filename: f.filename });
|
|
743
1391
|
}
|
|
744
1392
|
}
|
|
745
|
-
|
|
746
|
-
return [];
|
|
747
|
-
const results = await Promise.all(eligible.map(async (f) => {
|
|
1393
|
+
const nonOfficeResults = await Promise.all(nonOffice.map(async (f) => {
|
|
748
1394
|
try {
|
|
749
1395
|
const result = await this.hailerApi.downloadFile(f.fileId);
|
|
750
1396
|
if (IMAGE_MIME_TYPES.has(f.mime) && result.encoding === 'base64') {
|
|
@@ -768,11 +1414,98 @@ class Bot {
|
|
|
768
1414
|
}
|
|
769
1415
|
}
|
|
770
1416
|
catch (err) {
|
|
771
|
-
this.logger.warn('Failed to
|
|
1417
|
+
this.logger.warn('Failed to read file attachment', { fileId: f.fileId, mime: f.mime, err: err instanceof Error ? err.message : err });
|
|
1418
|
+
skipped.push({ filename: f.filename || f.fileId, reason: 'download failed' });
|
|
772
1419
|
}
|
|
773
1420
|
return null;
|
|
774
1421
|
}));
|
|
775
|
-
|
|
1422
|
+
// Download Office files in parallel (I/O-bound), parse serially under the lock (memory-bound)
|
|
1423
|
+
const officeDownloads = await Promise.all(office.map(async (f) => {
|
|
1424
|
+
try {
|
|
1425
|
+
const result = await this.hailerApi.downloadFile(f.fileId);
|
|
1426
|
+
return { f, result };
|
|
1427
|
+
}
|
|
1428
|
+
catch (err) {
|
|
1429
|
+
this.logger.warn('Failed to download Office file', { fileId: f.fileId, mime: f.mime, err: err instanceof Error ? err.message : err });
|
|
1430
|
+
skipped.push({ filename: f.filename || f.fileId, reason: 'download failed' });
|
|
1431
|
+
return null;
|
|
1432
|
+
}
|
|
1433
|
+
}));
|
|
1434
|
+
const officeResults = [];
|
|
1435
|
+
for (const entry of officeDownloads) {
|
|
1436
|
+
if (!entry) {
|
|
1437
|
+
continue;
|
|
1438
|
+
}
|
|
1439
|
+
const { f, result } = entry;
|
|
1440
|
+
if (result.encoding !== 'base64') {
|
|
1441
|
+
this.logger.warn('Office file has unexpected encoding, skipping', { fileId: f.fileId, mime: f.mime, encoding: result.encoding });
|
|
1442
|
+
skipped.push({ filename: f.filename || f.fileId, reason: 'unexpected encoding from download' });
|
|
1443
|
+
continue;
|
|
1444
|
+
}
|
|
1445
|
+
if (isHeapUnderPressure()) {
|
|
1446
|
+
this.logger.warn('Heap under pressure, skipping Office parse', { fileId: f.fileId, mime: f.mime });
|
|
1447
|
+
skipped.push({ filename: f.filename || f.fileId, reason: 'bot is under memory pressure — ask the user to retry in a moment' });
|
|
1448
|
+
continue;
|
|
1449
|
+
}
|
|
1450
|
+
const block = await withOfficeParseLock(async () => {
|
|
1451
|
+
try {
|
|
1452
|
+
if (EXCEL_MIME_TYPES.has(f.mime)) {
|
|
1453
|
+
const ExcelJS = (await Promise.resolve().then(() => __importStar(require('exceljs')))).default;
|
|
1454
|
+
const buffer = Buffer.from(result.content, 'base64');
|
|
1455
|
+
const wb = new ExcelJS.Workbook();
|
|
1456
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Node 24 Buffer<ArrayBuffer> vs exceljs Buffer typing mismatch
|
|
1457
|
+
await wb.xlsx.load(buffer);
|
|
1458
|
+
const parts = [];
|
|
1459
|
+
wb.eachSheet((sheet) => {
|
|
1460
|
+
const rows = [];
|
|
1461
|
+
sheet.eachRow({ includeEmpty: false }, (row) => {
|
|
1462
|
+
const values = Array.isArray(row.values) ? row.values.slice(1) : [];
|
|
1463
|
+
rows.push(values.map(cellToCsv).join(','));
|
|
1464
|
+
});
|
|
1465
|
+
parts.push(`=== Sheet: ${sheet.name} (${rows.length} rows) ===\n${rows.join('\n')}`);
|
|
1466
|
+
});
|
|
1467
|
+
return {
|
|
1468
|
+
type: 'document',
|
|
1469
|
+
source: { type: 'text', media_type: 'text/plain', data: capText(`${f.filename || 'spreadsheet'}\n\n${parts.join('\n\n')}`) },
|
|
1470
|
+
};
|
|
1471
|
+
}
|
|
1472
|
+
if (WORD_MIME_TYPES.has(f.mime)) {
|
|
1473
|
+
const mammoth = await Promise.resolve().then(() => __importStar(require('mammoth')));
|
|
1474
|
+
const buffer = Buffer.from(result.content, 'base64');
|
|
1475
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Node 24 Buffer<ArrayBuffer> vs mammoth Buffer typing mismatch
|
|
1476
|
+
const { value } = await mammoth.extractRawText({ buffer: buffer });
|
|
1477
|
+
return {
|
|
1478
|
+
type: 'document',
|
|
1479
|
+
source: { type: 'text', media_type: 'text/plain', data: capText(`${f.filename || 'document'}\n\n${value}`) },
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
catch (err) {
|
|
1484
|
+
this.logger.warn('Failed to parse Office file', { fileId: f.fileId, mime: f.mime, err: err instanceof Error ? err.message : err });
|
|
1485
|
+
skipped.push({ filename: f.filename || f.fileId, reason: 'parse error' });
|
|
1486
|
+
}
|
|
1487
|
+
return null;
|
|
1488
|
+
});
|
|
1489
|
+
if (block) {
|
|
1490
|
+
officeResults.push(block);
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
const blocks = [
|
|
1494
|
+
...nonOfficeResults.filter((b) => b !== null),
|
|
1495
|
+
...officeResults,
|
|
1496
|
+
];
|
|
1497
|
+
if (skipped.length > 0) {
|
|
1498
|
+
const lines = skipped.map((s) => ` - ${s.filename}: ${s.reason}`).join('\n');
|
|
1499
|
+
blocks.push({
|
|
1500
|
+
type: 'document',
|
|
1501
|
+
source: {
|
|
1502
|
+
type: 'text',
|
|
1503
|
+
media_type: 'text/plain',
|
|
1504
|
+
data: `=== SYSTEM NOTICE: some attachments could not be processed ===\nThe following files were attached but could NOT be read. Tell the user clearly which files failed and why. Do not pretend to have read them.\n${lines}`,
|
|
1505
|
+
},
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1508
|
+
return blocks;
|
|
776
1509
|
}
|
|
777
1510
|
async formatOutgoingMessage(text) {
|
|
778
1511
|
let formatted = await this.messageFormatter.resolveUserTags(text);
|
|
@@ -785,27 +1518,31 @@ class Bot {
|
|
|
785
1518
|
formatted = formatted.replace(/(\[hailerTag\|[^\]]+\]\([a-f0-9]{24}\)\uFEFF?)\s*\([^)]+\)/gi, '$1');
|
|
786
1519
|
// Strip hailerTags inside markdown table rows (pipe in hailerTag|Name breaks table columns)
|
|
787
1520
|
formatted = formatted.split('\n').map(line => {
|
|
788
|
-
if (!line.trimStart().startsWith('|'))
|
|
1521
|
+
if (!line.trimStart().startsWith('|')) {
|
|
789
1522
|
return line;
|
|
1523
|
+
}
|
|
790
1524
|
return line.replace(/\uFEFF?\[hailerTag\|([^\]]+)\]\([a-f0-9]{24}\)\uFEFF?/gi, '$1');
|
|
791
1525
|
}).join('\n');
|
|
792
1526
|
formatted = await this.resolveBareIds(formatted);
|
|
793
1527
|
return formatted;
|
|
794
1528
|
}
|
|
795
1529
|
async resolveBareIds(text) {
|
|
796
|
-
const bareIdPattern = /(?<!\()([a-f0-9]{24})(?!\)[
|
|
1530
|
+
const bareIdPattern = /(?<!\()([a-f0-9]{24})(?!\)[^[]*\[hailerTag)/gi;
|
|
797
1531
|
const matches = [...text.matchAll(bareIdPattern)];
|
|
798
1532
|
const idsToResolve = [];
|
|
799
1533
|
for (const match of matches) {
|
|
800
1534
|
const id = match[1];
|
|
801
|
-
if (new RegExp(`\\[hailerTag\\|[^\\]]+\\]\\(${id}\\)`, 'i').test(text))
|
|
1535
|
+
if (new RegExp(`\\[hailerTag\\|[^\\]]+\\]\\(${id}\\)`, 'i').test(text)) {
|
|
802
1536
|
continue;
|
|
1537
|
+
}
|
|
803
1538
|
idsToResolve.push(id);
|
|
804
|
-
if (idsToResolve.length >= 5)
|
|
1539
|
+
if (idsToResolve.length >= 5) {
|
|
805
1540
|
break;
|
|
1541
|
+
}
|
|
806
1542
|
}
|
|
807
|
-
if (idsToResolve.length === 0)
|
|
1543
|
+
if (idsToResolve.length === 0) {
|
|
808
1544
|
return text;
|
|
1545
|
+
}
|
|
809
1546
|
const resolutions = await Promise.all(idsToResolve.map(async (id) => {
|
|
810
1547
|
try {
|
|
811
1548
|
const result = await this.toolExecutor.execute('show_activity_by_id', { activityId: id }, this.getUserContext());
|
|
@@ -814,8 +1551,9 @@ class Bot {
|
|
|
814
1551
|
const j = resultText.match(/\{[\s\S]*\}/);
|
|
815
1552
|
if (j) {
|
|
816
1553
|
const p = JSON.parse(j[0]);
|
|
817
|
-
if (p.name)
|
|
1554
|
+
if (p.name) {
|
|
818
1555
|
return { id, name: p.name };
|
|
1556
|
+
}
|
|
819
1557
|
}
|
|
820
1558
|
}
|
|
821
1559
|
}
|
|
@@ -823,8 +1561,9 @@ class Bot {
|
|
|
823
1561
|
return null;
|
|
824
1562
|
}));
|
|
825
1563
|
for (const r of resolutions) {
|
|
826
|
-
if (!r)
|
|
1564
|
+
if (!r) {
|
|
827
1565
|
continue;
|
|
1566
|
+
}
|
|
828
1567
|
text = text.replace(r.id, `\uFEFF[hailerTag|${r.name}](${r.id})\uFEFF`);
|
|
829
1568
|
}
|
|
830
1569
|
return text;
|
|
@@ -832,15 +1571,17 @@ class Bot {
|
|
|
832
1571
|
// ===== HELPERS =====
|
|
833
1572
|
async injectPendingContext(discussionId, conversation) {
|
|
834
1573
|
const state = this.signalRouter.getState(discussionId);
|
|
835
|
-
if (!state || state.contextBuffer.length === 0)
|
|
1574
|
+
if (!state || state.contextBuffer.length === 0) {
|
|
836
1575
|
return;
|
|
1576
|
+
}
|
|
837
1577
|
const pending = state.contextBuffer.splice(0);
|
|
838
1578
|
const lastMsg = conversation[conversation.length - 1];
|
|
839
1579
|
if (lastMsg?.role === 'user' && Array.isArray(lastMsg.content)) {
|
|
840
1580
|
const content = lastMsg.content;
|
|
841
1581
|
content.push({ type: 'text', text: `<context type="messages-during-processing">\n${pending.map(m => this.formatIncomingMessage(m)).join('\n\n')}\n</context>` });
|
|
842
|
-
for (const block of await this.resolveFileAttachments(pending))
|
|
1582
|
+
for (const block of await this.resolveFileAttachments(pending)) {
|
|
843
1583
|
content.push(block);
|
|
1584
|
+
}
|
|
844
1585
|
this.opLogger.contextInject(discussionId, pending.length);
|
|
845
1586
|
}
|
|
846
1587
|
}
|
|
@@ -860,30 +1601,34 @@ class Bot {
|
|
|
860
1601
|
}
|
|
861
1602
|
}
|
|
862
1603
|
getToolStatus(toolUseBlocks) {
|
|
863
|
-
if (toolUseBlocks.length === 1)
|
|
1604
|
+
if (toolUseBlocks.length === 1) {
|
|
864
1605
|
return TOOL_STATUS_LABELS[toolUseBlocks[0].name] || toolUseBlocks[0].name.replace(/_/g, ' ');
|
|
1606
|
+
}
|
|
865
1607
|
return `${TOOL_STATUS_LABELS[toolUseBlocks[0].name] || toolUseBlocks[0].name.replace(/_/g, ' ')} (+${toolUseBlocks.length - 1} more)`;
|
|
866
1608
|
}
|
|
867
1609
|
getAnthropicTools() {
|
|
868
|
-
if (this.cachedTools)
|
|
1610
|
+
if (this.cachedTools) {
|
|
869
1611
|
return this.cachedTools;
|
|
1612
|
+
}
|
|
870
1613
|
const defs = this.toolExecutor.getToolDefinitions({
|
|
871
1614
|
allowedGroups: [tool_registry_1.ToolGroup.READ, tool_registry_1.ToolGroup.WRITE, tool_registry_1.ToolGroup.PLAYGROUND, tool_registry_1.ToolGroup.BOT_INTERNAL],
|
|
872
1615
|
});
|
|
873
|
-
const tools = defs.filter(d => BOT_TOOLS.has(d.name)).map(d => ({
|
|
1616
|
+
const tools = defs.filter(d => tool_profiles_1.BOT_TOOLS.has(d.name)).map(d => ({
|
|
874
1617
|
name: d.name, description: d.description, input_schema: d.inputSchema,
|
|
875
1618
|
}));
|
|
876
|
-
if (tools.length > 0)
|
|
1619
|
+
if (tools.length > 0) {
|
|
877
1620
|
tools[tools.length - 1].cache_control = { type: 'ephemeral' };
|
|
1621
|
+
}
|
|
878
1622
|
this.cachedTools = tools;
|
|
879
1623
|
return tools;
|
|
880
1624
|
}
|
|
881
1625
|
async sendMessage(discussionId, text, links) {
|
|
882
1626
|
try {
|
|
883
1627
|
const msgData = { msg: text };
|
|
884
|
-
if (links?.length)
|
|
1628
|
+
if (links?.length) {
|
|
885
1629
|
msgData.links = links;
|
|
886
|
-
|
|
1630
|
+
}
|
|
1631
|
+
await this.client.socket.request(hailer_rpc_1.HailerRpc.Messenger.send, [msgData, discussionId]);
|
|
887
1632
|
this.opLogger.messageOut(discussionId, text.length, (text.match(/\[hailerTag\|/g) || []).length);
|
|
888
1633
|
}
|
|
889
1634
|
catch (error) {
|
|
@@ -891,13 +1636,15 @@ class Bot {
|
|
|
891
1636
|
}
|
|
892
1637
|
}
|
|
893
1638
|
getUserContext() {
|
|
894
|
-
if (!this.userContext)
|
|
1639
|
+
if (!this.userContext) {
|
|
895
1640
|
throw new Error('Bot not started');
|
|
1641
|
+
}
|
|
896
1642
|
return this.userContext;
|
|
897
1643
|
}
|
|
898
1644
|
trackTokenUsage(response, message, model) {
|
|
899
|
-
if (!response.usage)
|
|
1645
|
+
if (!response.usage) {
|
|
900
1646
|
return;
|
|
1647
|
+
}
|
|
901
1648
|
const { input_tokens, output_tokens } = response.usage;
|
|
902
1649
|
const cacheCreation = response.usage.cache_creation_input_tokens || 0;
|
|
903
1650
|
const cacheRead = response.usage.cache_read_input_tokens || 0;
|