crewly 1.12.1 → 1.13.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/config/skills/_common/complete-body-shape.test.sh +113 -0
- package/config/skills/_common/no-dangling-symlinks.test.sh +63 -0
- package/config/skills/_common/task-pool-body-shape.test.sh +471 -0
- package/config/skills/agent/core/accept-task/execute.sh +30 -5
- package/config/skills/agent/core/block-task/execute.sh +59 -6
- package/config/skills/agent/core/complete-task/SKILL.md +37 -5
- package/config/skills/agent/core/complete-task/execute.sh +40 -9
- package/config/skills/agent/core/complete-task/execute.test.sh +213 -0
- package/config/skills/agent/core/create-task/SKILL.md +33 -0
- package/config/skills/agent/core/create-task/execute.sh +82 -23
- package/config/skills/agent/core/delegate-task/execute.sh +46 -0
- package/config/skills/agent/core/get-team-norms/SKILL.md +1 -1
- package/config/skills/agent/core/get-team-norms/execute.sh +73 -2
- package/config/skills/agent/core/handoff-task/execute.sh +8 -1
- package/config/skills/agent/core/report-status/SKILL.md +26 -0
- package/config/skills/agent/core/report-status/execute.sh +40 -5
- package/config/skills/orchestrator/delegate-task/SKILL.md +1 -1
- package/config/skills/orchestrator/delegate-task/execute.sh +45 -11
- package/config/skills/orchestrator/delegate-task/execute.test.sh +153 -1
- package/config/skills/team-leader/aggregate-results/SKILL.md +1 -1
- package/config/skills/team-leader/decompose-goal/execute.sh +11 -16
- package/config/skills/team-leader/delegate-task/SKILL.md +14 -1
- package/config/skills/team-leader/delegate-task/execute.sh +132 -43
- package/config/skills/team-leader/delegate-task/execute.test.sh +180 -27
- package/config/vitest-exclude.constants.ts +56 -0
- package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat/chat.controller.js +29 -7
- package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/cloud/cloud.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/cloud/cloud.controller.js +1 -1
- package/dist/backend/backend/src/controllers/cloud/cloud.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/monitoring/terminal.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js +18 -3
- package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack.controller.js +18 -15
- package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts +6 -1
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js +19 -10
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +20 -0
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/scripts/stamp-build.d.ts +25 -0
- package/dist/backend/backend/src/scripts/stamp-build.d.ts.map +1 -0
- package/dist/backend/backend/src/scripts/stamp-build.js +48 -0
- package/dist/backend/backend/src/scripts/stamp-build.js.map +1 -0
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.js +33 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts +78 -2
- package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js +248 -44
- package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/types.d.ts +51 -3
- package/dist/backend/backend/src/services/agent/crewly-agent/types.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/types.js +31 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/types.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-client.service.d.ts +5 -0
- package/dist/backend/backend/src/services/cloud/cloud-client.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-client.service.js +7 -0
- package/dist/backend/backend/src/services/cloud/cloud-client.service.js.map +1 -1
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.d.ts +15 -0
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.js +40 -0
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.js.map +1 -1
- package/dist/backend/backend/src/services/orchestrator/commitment-approval-guard.d.ts.map +1 -1
- package/dist/backend/backend/src/services/orchestrator/commitment-approval-guard.js +26 -0
- package/dist/backend/backend/src/services/orchestrator/commitment-approval-guard.js.map +1 -1
- package/dist/backend/backend/src/services/orchestrator/onboarding/materialize-team.d.ts.map +1 -1
- package/dist/backend/backend/src/services/orchestrator/onboarding/materialize-team.js +19 -3
- package/dist/backend/backend/src/services/orchestrator/onboarding/materialize-team.js.map +1 -1
- package/dist/backend/backend/src/services/orchestrator/orchestrator-status.service.d.ts +28 -3
- package/dist/backend/backend/src/services/orchestrator/orchestrator-status.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/orchestrator/orchestrator-status.service.js +65 -8
- package/dist/backend/backend/src/services/orchestrator/orchestrator-status.service.js.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts +214 -4
- package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconcile-rules.js +330 -48
- package/dist/backend/backend/src/services/reconciler/reconcile-rules.js.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts +13 -1
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js +84 -5
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler.service.d.ts +14 -0
- package/dist/backend/backend/src/services/reconciler/reconciler.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler.service.js +77 -3
- package/dist/backend/backend/src/services/reconciler/reconciler.service.js.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +152 -7
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js +268 -36
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/escalation-router.service.js +2 -2
- package/dist/backend/backend/src/services/v3/escalation-router.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/request-sla.subscriber.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/request-sla.subscriber.js +39 -2
- package/dist/backend/backend/src/services/v3/request-sla.subscriber.js.map +1 -1
- package/dist/backend/backend/src/services/wiki/wiki-migrate.service.d.ts +3 -0
- package/dist/backend/backend/src/services/wiki/wiki-migrate.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/wiki/wiki-migrate.service.js +21 -10
- package/dist/backend/backend/src/services/wiki/wiki-migrate.service.js.map +1 -1
- package/dist/backend/backend/src/types/v2/index.d.ts +1 -1
- package/dist/backend/backend/src/types/v2/index.d.ts.map +1 -1
- package/dist/backend/backend/src/types/v2/index.js +1 -1
- package/dist/backend/backend/src/types/v2/index.js.map +1 -1
- package/dist/backend/backend/src/types/v2/work-item.types.d.ts +181 -1
- package/dist/backend/backend/src/types/v2/work-item.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/v2/work-item.types.js +104 -17
- package/dist/backend/backend/src/types/v2/work-item.types.js.map +1 -1
- package/dist/backend/backend/src/utils/build-provenance.d.ts +150 -0
- package/dist/backend/backend/src/utils/build-provenance.d.ts.map +1 -0
- package/dist/backend/backend/src/utils/build-provenance.js +211 -0
- package/dist/backend/backend/src/utils/build-provenance.js.map +1 -0
- package/dist/backend/backend/src/websocket/chat.gateway.d.ts.map +1 -1
- package/dist/backend/backend/src/websocket/chat.gateway.js +7 -1
- package/dist/backend/backend/src/websocket/chat.gateway.js.map +1 -1
- package/dist/backend/build-info.json +4 -0
- package/dist/backend/config/vitest-exclude.constants.d.ts +53 -0
- package/dist/backend/config/vitest-exclude.constants.d.ts.map +1 -0
- package/dist/backend/config/vitest-exclude.constants.js +55 -0
- package/dist/backend/config/vitest-exclude.constants.js.map +1 -0
- package/dist/cli/backend/src/services/agent/active-work-briefing.service.d.ts +538 -0
- package/dist/cli/backend/src/services/agent/active-work-briefing.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/active-work-briefing.service.js +802 -0
- package/dist/cli/backend/src/services/agent/active-work-briefing.service.js.map +1 -0
- package/dist/cli/backend/src/services/agent/agent-heartbeat.service.d.ts +240 -0
- package/dist/cli/backend/src/services/agent/agent-heartbeat.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/agent-heartbeat.service.js +524 -0
- package/dist/cli/backend/src/services/agent/agent-heartbeat.service.js.map +1 -0
- package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts +629 -0
- package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/agent-registration.service.js +4548 -0
- package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -0
- package/dist/cli/backend/src/services/agent/agent-suspend.service.d.ts +118 -0
- package/dist/cli/backend/src/services/agent/agent-suspend.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/agent-suspend.service.js +304 -0
- package/dist/cli/backend/src/services/agent/agent-suspend.service.js.map +1 -0
- package/dist/cli/backend/src/services/agent/auditor-scheduler.service.d.ts +196 -0
- package/dist/cli/backend/src/services/agent/auditor-scheduler.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/auditor-scheduler.service.js +504 -0
- package/dist/cli/backend/src/services/agent/auditor-scheduler.service.js.map +1 -0
- package/dist/cli/backend/src/services/agent/claude-runtime.service.d.ts +97 -0
- package/dist/cli/backend/src/services/agent/claude-runtime.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/claude-runtime.service.js +334 -0
- package/dist/cli/backend/src/services/agent/claude-runtime.service.js.map +1 -0
- package/dist/cli/backend/src/services/agent/codex-runtime.service.d.ts +47 -0
- package/dist/cli/backend/src/services/agent/codex-runtime.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/codex-runtime.service.js +112 -0
- package/dist/cli/backend/src/services/agent/codex-runtime.service.js.map +1 -0
- package/dist/cli/backend/src/services/agent/context-window-monitor.service.d.ts +323 -0
- package/dist/cli/backend/src/services/agent/context-window-monitor.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/context-window-monitor.service.js +1056 -0
- package/dist/cli/backend/src/services/agent/context-window-monitor.service.js.map +1 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts +198 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js +816 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js.map +1 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/in-process-log-buffer.d.ts +155 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/in-process-log-buffer.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/in-process-log-buffer.js +272 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/in-process-log-buffer.js.map +1 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/in-process-runtime-registry.d.ts +91 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/in-process-runtime-registry.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/in-process-runtime-registry.js +120 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/in-process-runtime-registry.js.map +1 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/types.d.ts +536 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/types.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/types.js +282 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/types.js.map +1 -0
- package/dist/cli/backend/src/services/agent/disk-cleanup.service.d.ts +100 -0
- package/dist/cli/backend/src/services/agent/disk-cleanup.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/disk-cleanup.service.js +149 -0
- package/dist/cli/backend/src/services/agent/disk-cleanup.service.js.map +1 -0
- package/dist/cli/backend/src/services/agent/gemini-runtime.service.d.ts +130 -0
- package/dist/cli/backend/src/services/agent/gemini-runtime.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/gemini-runtime.service.js +758 -0
- package/dist/cli/backend/src/services/agent/gemini-runtime.service.js.map +1 -0
- package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.d.ts +216 -0
- package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.js +508 -0
- package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.js.map +1 -0
- package/dist/cli/backend/src/services/agent/pty-activity-tracker.service.d.ts +113 -0
- package/dist/cli/backend/src/services/agent/pty-activity-tracker.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/pty-activity-tracker.service.js +161 -0
- package/dist/cli/backend/src/services/agent/pty-activity-tracker.service.js.map +1 -0
- package/dist/cli/backend/src/services/agent/runtime-agent.service.abstract.d.ts +156 -0
- package/dist/cli/backend/src/services/agent/runtime-agent.service.abstract.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/runtime-agent.service.abstract.js +641 -0
- package/dist/cli/backend/src/services/agent/runtime-agent.service.abstract.js.map +1 -0
- package/dist/cli/backend/src/services/agent/runtime-exit-monitor.service.d.ts +295 -0
- package/dist/cli/backend/src/services/agent/runtime-exit-monitor.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/runtime-exit-monitor.service.js +1166 -0
- package/dist/cli/backend/src/services/agent/runtime-exit-monitor.service.js.map +1 -0
- package/dist/cli/backend/src/services/agent/runtime-service.factory.d.ts +71 -0
- package/dist/cli/backend/src/services/agent/runtime-service.factory.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/runtime-service.factory.js +174 -0
- package/dist/cli/backend/src/services/agent/runtime-service.factory.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-builder.service.d.ts +435 -0
- package/dist/cli/backend/src/services/ai/prompt-builder.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-builder.service.js +1331 -0
- package/dist/cli/backend/src/services/ai/prompt-builder.service.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/capability-overlay.module.d.ts +33 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/capability-overlay.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/capability-overlay.module.js +70 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/capability-overlay.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/communication.module.d.ts +100 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/communication.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/communication.module.js +296 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/communication.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.d.ts +59 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.js +87 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/default-execution-loop.module.d.ts +67 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/default-execution-loop.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/default-execution-loop.module.js +84 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/default-execution-loop.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/domain-sop.module.d.ts +35 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/domain-sop.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/domain-sop.module.js +50 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/domain-sop.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/expert-profile.module.d.ts +41 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/expert-profile.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/expert-profile.module.js +95 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/expert-profile.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/identity.module.d.ts +28 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/identity.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/identity.module.js +47 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/identity.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/lazy-anti-patterns.module.d.ts +65 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/lazy-anti-patterns.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/lazy-anti-patterns.module.js +79 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/lazy-anti-patterns.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/learning-reference.module.d.ts +28 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/learning-reference.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/learning-reference.module.js +55 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/learning-reference.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/lifecycle.module.d.ts +44 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/lifecycle.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/lifecycle.module.js +120 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/lifecycle.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/markdown-file-module.d.ts +46 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/markdown-file-module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/markdown-file-module.js +44 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/markdown-file-module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/memory-reference.module.d.ts +28 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/memory-reference.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/memory-reference.module.js +46 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/memory-reference.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/mission-context.module.d.ts +60 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/mission-context.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/mission-context.module.js +104 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/mission-context.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/project-reference.module.d.ts +29 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/project-reference.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/project-reference.module.js +50 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/project-reference.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/prompt-assembly.service.d.ts +103 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/prompt-assembly.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/prompt-assembly.service.js +383 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/prompt-assembly.service.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/recovery.module.d.ts +34 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/recovery.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/recovery.module.js +96 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/recovery.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/request-contract.module.d.ts +106 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/request-contract.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/request-contract.module.js +167 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/request-contract.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/risk-policy.module.d.ts +35 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/risk-policy.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/risk-policy.module.js +50 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/risk-policy.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/role-boundary.module.d.ts +91 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/role-boundary.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/role-boundary.module.js +411 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/role-boundary.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts +62 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +199 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/sop-norm-distinction.module.d.ts +79 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/sop-norm-distinction.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/sop-norm-distinction.module.js +118 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/sop-norm-distinction.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/soul.module.d.ts +102 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/soul.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/soul.module.js +229 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/soul.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/team-norms.module.d.ts +61 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/team-norms.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/team-norms.module.js +197 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/team-norms.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/team-reference.module.d.ts +54 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/team-reference.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/team-reference.module.js +149 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/team-reference.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/user-profile-reference.module.d.ts +32 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/user-profile-reference.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/user-profile-reference.module.js +69 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/user-profile-reference.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/working-memory.module.d.ts +91 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/working-memory.module.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/working-memory.module.js +136 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/working-memory.module.js.map +1 -0
- package/dist/cli/backend/src/services/ai/self-improvement/growth-areas.service.d.ts +81 -0
- package/dist/cli/backend/src/services/ai/self-improvement/growth-areas.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/ai/self-improvement/growth-areas.service.js +135 -0
- package/dist/cli/backend/src/services/ai/self-improvement/growth-areas.service.js.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts +737 -0
- package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js +1213 -0
- package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/chat-v2.singleton.d.ts +27 -0
- package/dist/cli/backend/src/services/chat-v2/chat-v2.singleton.d.ts.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/chat-v2.singleton.js +57 -0
- package/dist/cli/backend/src/services/chat-v2/chat-v2.singleton.js.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/config.d.ts +100 -0
- package/dist/cli/backend/src/services/chat-v2/config.d.ts.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/config.js +174 -0
- package/dist/cli/backend/src/services/chat-v2/config.js.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/legacy-dto.utils.d.ts +118 -0
- package/dist/cli/backend/src/services/chat-v2/legacy-dto.utils.d.ts.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/legacy-dto.utils.js +171 -0
- package/dist/cli/backend/src/services/chat-v2/legacy-dto.utils.js.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/channel.store.d.ts +188 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/channel.store.d.ts.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/channel.store.js +317 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/channel.store.js.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/chat-db.d.ts +119 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/chat-db.d.ts.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/chat-db.js +551 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/chat-db.js.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.d.ts +208 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.d.ts.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.js +405 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.js.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/types.d.ts +341 -0
- package/dist/cli/backend/src/services/chat-v2/types.d.ts.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/types.js +61 -0
- package/dist/cli/backend/src/services/chat-v2/types.js.map +1 -0
- package/dist/cli/backend/src/services/cloud/cloud-client.service.d.ts +5 -0
- package/dist/cli/backend/src/services/cloud/cloud-client.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-client.service.js +7 -0
- package/dist/cli/backend/src/services/cloud/cloud-client.service.js.map +1 -1
- package/dist/cli/backend/src/services/continuation/patterns/idle-patterns.d.ts +37 -0
- package/dist/cli/backend/src/services/continuation/patterns/idle-patterns.d.ts.map +1 -0
- package/dist/cli/backend/src/services/continuation/patterns/idle-patterns.js +63 -0
- package/dist/cli/backend/src/services/continuation/patterns/idle-patterns.js.map +1 -0
- package/dist/cli/backend/src/services/intent-task/intent-classifier.rules.d.ts +335 -0
- package/dist/cli/backend/src/services/intent-task/intent-classifier.rules.d.ts.map +1 -0
- package/dist/cli/backend/src/services/intent-task/intent-classifier.rules.js +421 -0
- package/dist/cli/backend/src/services/intent-task/intent-classifier.rules.js.map +1 -0
- package/dist/cli/backend/src/services/mcp-client.d.ts +233 -0
- package/dist/cli/backend/src/services/mcp-client.d.ts.map +1 -0
- package/dist/cli/backend/src/services/mcp-client.js +302 -0
- package/dist/cli/backend/src/services/mcp-client.js.map +1 -0
- package/dist/cli/backend/src/services/memory/mission-context.service.d.ts +168 -0
- package/dist/cli/backend/src/services/memory/mission-context.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/memory/mission-context.service.js +365 -0
- package/dist/cli/backend/src/services/memory/mission-context.service.js.map +1 -0
- package/dist/cli/backend/src/services/memory/session-memory.service.d.ts +197 -0
- package/dist/cli/backend/src/services/memory/session-memory.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/memory/session-memory.service.js +369 -0
- package/dist/cli/backend/src/services/memory/session-memory.service.js.map +1 -0
- package/dist/cli/backend/src/services/memory/user-profile.service.d.ts +77 -0
- package/dist/cli/backend/src/services/memory/user-profile.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/memory/user-profile.service.js +171 -0
- package/dist/cli/backend/src/services/memory/user-profile.service.js.map +1 -0
- package/dist/cli/backend/src/services/memory/working-memory.service.d.ts +232 -0
- package/dist/cli/backend/src/services/memory/working-memory.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/memory/working-memory.service.js +417 -0
- package/dist/cli/backend/src/services/memory/working-memory.service.js.map +1 -0
- package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.d.ts +84 -0
- package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.js +136 -0
- package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.js.map +1 -0
- package/dist/cli/backend/src/services/messaging/thread-status-queue.service.d.ts +358 -0
- package/dist/cli/backend/src/services/messaging/thread-status-queue.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/messaging/thread-status-queue.service.js +755 -0
- package/dist/cli/backend/src/services/messaging/thread-status-queue.service.js.map +1 -0
- package/dist/cli/backend/src/services/monitoring/token-usage.service.d.ts +269 -0
- package/dist/cli/backend/src/services/monitoring/token-usage.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/monitoring/token-usage.service.js +391 -0
- package/dist/cli/backend/src/services/monitoring/token-usage.service.js.map +1 -0
- package/dist/cli/backend/src/services/observability/agent-behavior-log.service.d.ts +132 -0
- package/dist/cli/backend/src/services/observability/agent-behavior-log.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/observability/agent-behavior-log.service.js +284 -0
- package/dist/cli/backend/src/services/observability/agent-behavior-log.service.js.map +1 -0
- package/dist/cli/backend/src/services/observability/agent-behavior-log.singleton.d.ts +70 -0
- package/dist/cli/backend/src/services/observability/agent-behavior-log.singleton.d.ts.map +1 -0
- package/dist/cli/backend/src/services/observability/agent-behavior-log.singleton.js +121 -0
- package/dist/cli/backend/src/services/observability/agent-behavior-log.singleton.js.map +1 -0
- package/dist/cli/backend/src/services/observability/agent-behavior-log.types.d.ts +130 -0
- package/dist/cli/backend/src/services/observability/agent-behavior-log.types.d.ts.map +1 -0
- package/dist/cli/backend/src/services/observability/agent-behavior-log.types.js +48 -0
- package/dist/cli/backend/src/services/observability/agent-behavior-log.types.js.map +1 -0
- package/dist/cli/backend/src/services/observability/observability-db.d.ts +84 -0
- package/dist/cli/backend/src/services/observability/observability-db.d.ts.map +1 -0
- package/dist/cli/backend/src/services/observability/observability-db.js +165 -0
- package/dist/cli/backend/src/services/observability/observability-db.js.map +1 -0
- package/dist/cli/backend/src/services/onboarding/content-approval.service.d.ts +124 -0
- package/dist/cli/backend/src/services/onboarding/content-approval.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/onboarding/content-approval.service.js +256 -0
- package/dist/cli/backend/src/services/onboarding/content-approval.service.js.map +1 -0
- package/dist/cli/backend/src/services/onboarding/content-approval.types.d.ts +80 -0
- package/dist/cli/backend/src/services/onboarding/content-approval.types.d.ts.map +1 -0
- package/dist/cli/backend/src/services/onboarding/content-approval.types.js +16 -0
- package/dist/cli/backend/src/services/onboarding/content-approval.types.js.map +1 -0
- package/dist/cli/backend/src/services/orc/orc-delivery-enforcer.service.d.ts +142 -0
- package/dist/cli/backend/src/services/orc/orc-delivery-enforcer.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/orc/orc-delivery-enforcer.service.js +265 -0
- package/dist/cli/backend/src/services/orc/orc-delivery-enforcer.service.js.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/improvement-marker.service.d.ts +231 -0
- package/dist/cli/backend/src/services/orchestrator/improvement-marker.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/improvement-marker.service.js +353 -0
- package/dist/cli/backend/src/services/orchestrator/improvement-marker.service.js.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/improvement-startup.service.d.ts +157 -0
- package/dist/cli/backend/src/services/orchestrator/improvement-startup.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/improvement-startup.service.js +483 -0
- package/dist/cli/backend/src/services/orchestrator/improvement-startup.service.js.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/index.d.ts +16 -0
- package/dist/cli/backend/src/services/orchestrator/index.d.ts.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/index.js +16 -0
- package/dist/cli/backend/src/services/orchestrator/index.js.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/orchestrator-restart.service.d.ts +117 -0
- package/dist/cli/backend/src/services/orchestrator/orchestrator-restart.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/orchestrator-restart.service.js +300 -0
- package/dist/cli/backend/src/services/orchestrator/orchestrator-restart.service.js.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/orchestrator-setup.service.d.ts +114 -0
- package/dist/cli/backend/src/services/orchestrator/orchestrator-setup.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/orchestrator-setup.service.js +195 -0
- package/dist/cli/backend/src/services/orchestrator/orchestrator-setup.service.js.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/orchestrator-status.service.d.ts +94 -0
- package/dist/cli/backend/src/services/orchestrator/orchestrator-status.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/orchestrator-status.service.js +279 -0
- package/dist/cli/backend/src/services/orchestrator/orchestrator-status.service.js.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/safe-restart.service.d.ts +192 -0
- package/dist/cli/backend/src/services/orchestrator/safe-restart.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/safe-restart.service.js +395 -0
- package/dist/cli/backend/src/services/orchestrator/safe-restart.service.js.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/self-improvement.service.d.ts +201 -0
- package/dist/cli/backend/src/services/orchestrator/self-improvement.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/self-improvement.service.js +396 -0
- package/dist/cli/backend/src/services/orchestrator/self-improvement.service.js.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/slack-bridge-lazy.d.ts +17 -0
- package/dist/cli/backend/src/services/orchestrator/slack-bridge-lazy.d.ts.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/slack-bridge-lazy.js +30 -0
- package/dist/cli/backend/src/services/orchestrator/slack-bridge-lazy.js.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/state-persistence.service.d.ts +249 -0
- package/dist/cli/backend/src/services/orchestrator/state-persistence.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/orchestrator/state-persistence.service.js +569 -0
- package/dist/cli/backend/src/services/orchestrator/state-persistence.service.js.map +1 -0
- package/dist/cli/backend/src/services/session/index.d.ts +47 -0
- package/dist/cli/backend/src/services/session/index.d.ts.map +1 -0
- package/dist/cli/backend/src/services/session/index.js +50 -0
- package/dist/cli/backend/src/services/session/index.js.map +1 -0
- package/dist/cli/backend/src/services/session/log-rotation.service.d.ts +163 -0
- package/dist/cli/backend/src/services/session/log-rotation.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/session/log-rotation.service.js +362 -0
- package/dist/cli/backend/src/services/session/log-rotation.service.js.map +1 -0
- package/dist/cli/backend/src/services/session/pty/index.d.ts +11 -0
- package/dist/cli/backend/src/services/session/pty/index.d.ts.map +1 -0
- package/dist/cli/backend/src/services/session/pty/index.js +11 -0
- package/dist/cli/backend/src/services/session/pty/index.js.map +1 -0
- package/dist/cli/backend/src/services/session/pty/pty-session-backend.d.ts +445 -0
- package/dist/cli/backend/src/services/session/pty/pty-session-backend.d.ts.map +1 -0
- package/dist/cli/backend/src/services/session/pty/pty-session-backend.js +804 -0
- package/dist/cli/backend/src/services/session/pty/pty-session-backend.js.map +1 -0
- package/dist/cli/backend/src/services/session/pty/pty-session.d.ts +252 -0
- package/dist/cli/backend/src/services/session/pty/pty-session.d.ts.map +1 -0
- package/dist/cli/backend/src/services/session/pty/pty-session.js +611 -0
- package/dist/cli/backend/src/services/session/pty/pty-session.js.map +1 -0
- package/dist/cli/backend/src/services/session/pty/pty-terminal-buffer.d.ts +275 -0
- package/dist/cli/backend/src/services/session/pty/pty-terminal-buffer.d.ts.map +1 -0
- package/dist/cli/backend/src/services/session/pty/pty-terminal-buffer.js +403 -0
- package/dist/cli/backend/src/services/session/pty/pty-terminal-buffer.js.map +1 -0
- package/dist/cli/backend/src/services/session/runtime-pid-registry.service.d.ts +86 -0
- package/dist/cli/backend/src/services/session/runtime-pid-registry.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/session/runtime-pid-registry.service.js +214 -0
- package/dist/cli/backend/src/services/session/runtime-pid-registry.service.js.map +1 -0
- package/dist/cli/backend/src/services/session/session-backend.factory.d.ts +127 -0
- package/dist/cli/backend/src/services/session/session-backend.factory.d.ts.map +1 -0
- package/dist/cli/backend/src/services/session/session-backend.factory.js +226 -0
- package/dist/cli/backend/src/services/session/session-backend.factory.js.map +1 -0
- package/dist/cli/backend/src/services/session/session-backend.interface.d.ts +390 -0
- package/dist/cli/backend/src/services/session/session-backend.interface.d.ts.map +1 -0
- package/dist/cli/backend/src/services/session/session-backend.interface.js +19 -0
- package/dist/cli/backend/src/services/session/session-backend.interface.js.map +1 -0
- package/dist/cli/backend/src/services/session/session-command-helper.d.ts +350 -0
- package/dist/cli/backend/src/services/session/session-command-helper.d.ts.map +1 -0
- package/dist/cli/backend/src/services/session/session-command-helper.js +810 -0
- package/dist/cli/backend/src/services/session/session-command-helper.js.map +1 -0
- package/dist/cli/backend/src/services/session/session-handoff.service.d.ts +307 -0
- package/dist/cli/backend/src/services/session/session-handoff.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/session/session-handoff.service.js +742 -0
- package/dist/cli/backend/src/services/session/session-handoff.service.js.map +1 -0
- package/dist/cli/backend/src/services/session/session-state-persistence.d.ts +231 -0
- package/dist/cli/backend/src/services/session/session-state-persistence.d.ts.map +1 -0
- package/dist/cli/backend/src/services/session/session-state-persistence.js +407 -0
- package/dist/cli/backend/src/services/session/session-state-persistence.js.map +1 -0
- package/dist/cli/backend/src/services/settings/role.service.d.ts +277 -0
- package/dist/cli/backend/src/services/settings/role.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/settings/role.service.js +728 -0
- package/dist/cli/backend/src/services/settings/role.service.js.map +1 -0
- package/dist/cli/backend/src/services/slack/cross-machine-message.service.d.ts +152 -0
- package/dist/cli/backend/src/services/slack/cross-machine-message.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/slack/cross-machine-message.service.js +354 -0
- package/dist/cli/backend/src/services/slack/cross-machine-message.service.js.map +1 -0
- package/dist/cli/backend/src/services/slack/slack-image.service.d.ts +114 -0
- package/dist/cli/backend/src/services/slack/slack-image.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/slack/slack-image.service.js +331 -0
- package/dist/cli/backend/src/services/slack/slack-image.service.js.map +1 -0
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts +499 -0
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -0
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js +1928 -0
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -0
- package/dist/cli/backend/src/services/slack/slack.service.d.ts +315 -0
- package/dist/cli/backend/src/services/slack/slack.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/slack/slack.service.js +1094 -0
- package/dist/cli/backend/src/services/slack/slack.service.js.map +1 -0
- package/dist/cli/backend/src/services/sop/sop.service.d.ts +364 -0
- package/dist/cli/backend/src/services/sop/sop.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/sop/sop.service.js +734 -0
- package/dist/cli/backend/src/services/sop/sop.service.js.map +1 -0
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +152 -7
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js +268 -36
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/cli/backend/src/services/v3/escalation-router.service.d.ts +178 -0
- package/dist/cli/backend/src/services/v3/escalation-router.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/v3/escalation-router.service.js +692 -0
- package/dist/cli/backend/src/services/v3/escalation-router.service.js.map +1 -0
- package/dist/cli/backend/src/services/v3/kr-tracking.service.d.ts +200 -0
- package/dist/cli/backend/src/services/v3/kr-tracking.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/v3/kr-tracking.service.js +482 -0
- package/dist/cli/backend/src/services/v3/kr-tracking.service.js.map +1 -0
- package/dist/cli/backend/src/services/v3/mission-executor.service.d.ts +117 -0
- package/dist/cli/backend/src/services/v3/mission-executor.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/v3/mission-executor.service.js +321 -0
- package/dist/cli/backend/src/services/v3/mission-executor.service.js.map +1 -0
- package/dist/cli/backend/src/services/v3/request-sla.subscriber.d.ts +610 -0
- package/dist/cli/backend/src/services/v3/request-sla.subscriber.d.ts.map +1 -0
- package/dist/cli/backend/src/services/v3/request-sla.subscriber.js +1529 -0
- package/dist/cli/backend/src/services/v3/request-sla.subscriber.js.map +1 -0
- package/dist/cli/backend/src/services/v3/request-tracker.service.d.ts +78 -0
- package/dist/cli/backend/src/services/v3/request-tracker.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/v3/request-tracker.service.js +108 -0
- package/dist/cli/backend/src/services/v3/request-tracker.service.js.map +1 -0
- package/dist/cli/backend/src/services/v3/request.service.d.ts +295 -0
- package/dist/cli/backend/src/services/v3/request.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/v3/request.service.js +534 -0
- package/dist/cli/backend/src/services/v3/request.service.js.map +1 -0
- package/dist/cli/backend/src/services/v3/v3-data.service.d.ts +340 -0
- package/dist/cli/backend/src/services/v3/v3-data.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/v3/v3-data.service.js +1113 -0
- package/dist/cli/backend/src/services/v3/v3-data.service.js.map +1 -0
- package/dist/cli/backend/src/types/cross-machine.types.d.ts +103 -0
- package/dist/cli/backend/src/types/cross-machine.types.d.ts.map +1 -0
- package/dist/cli/backend/src/types/cross-machine.types.js +47 -0
- package/dist/cli/backend/src/types/cross-machine.types.js.map +1 -0
- package/dist/cli/backend/src/types/intent-task.types.d.ts +402 -0
- package/dist/cli/backend/src/types/intent-task.types.d.ts.map +1 -0
- package/dist/cli/backend/src/types/intent-task.types.js +403 -0
- package/dist/cli/backend/src/types/intent-task.types.js.map +1 -0
- package/dist/cli/backend/src/types/orchestrator-state.types.d.ts +531 -0
- package/dist/cli/backend/src/types/orchestrator-state.types.d.ts.map +1 -0
- package/dist/cli/backend/src/types/orchestrator-state.types.js +110 -0
- package/dist/cli/backend/src/types/orchestrator-state.types.js.map +1 -0
- package/dist/cli/backend/src/types/v2/index.d.ts +30 -0
- package/dist/cli/backend/src/types/v2/index.d.ts.map +1 -0
- package/dist/cli/backend/src/types/v2/index.js +22 -0
- package/dist/cli/backend/src/types/v2/index.js.map +1 -0
- package/dist/cli/backend/src/types/v2/key-result.types.d.ts +279 -0
- package/dist/cli/backend/src/types/v2/key-result.types.d.ts.map +1 -0
- package/dist/cli/backend/src/types/v2/key-result.types.js +264 -0
- package/dist/cli/backend/src/types/v2/key-result.types.js.map +1 -0
- package/dist/cli/backend/src/types/v2/mission.types.d.ts +661 -0
- package/dist/cli/backend/src/types/v2/mission.types.d.ts.map +1 -0
- package/dist/cli/backend/src/types/v2/mission.types.js +713 -0
- package/dist/cli/backend/src/types/v2/mission.types.js.map +1 -0
- package/dist/cli/backend/src/types/v2/reconcile.types.d.ts +204 -0
- package/dist/cli/backend/src/types/v2/reconcile.types.d.ts.map +1 -0
- package/dist/cli/backend/src/types/v2/reconcile.types.js +121 -0
- package/dist/cli/backend/src/types/v2/reconcile.types.js.map +1 -0
- package/dist/cli/backend/src/types/v2/request.types.d.ts +192 -0
- package/dist/cli/backend/src/types/v2/request.types.d.ts.map +1 -0
- package/dist/cli/backend/src/types/v2/request.types.js +179 -0
- package/dist/cli/backend/src/types/v2/request.types.js.map +1 -0
- package/dist/cli/backend/src/types/v2/work-item.types.d.ts +181 -1
- package/dist/cli/backend/src/types/v2/work-item.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/v2/work-item.types.js +104 -17
- package/dist/cli/backend/src/types/v2/work-item.types.js.map +1 -1
- package/dist/cli/backend/src/types/v2/workspace.types.d.ts +157 -0
- package/dist/cli/backend/src/types/v2/workspace.types.d.ts.map +1 -0
- package/dist/cli/backend/src/types/v2/workspace.types.js +173 -0
- package/dist/cli/backend/src/types/v2/workspace.types.js.map +1 -0
- package/dist/cli/backend/src/utils/async.utils.d.ts +68 -0
- package/dist/cli/backend/src/utils/async.utils.d.ts.map +1 -0
- package/dist/cli/backend/src/utils/async.utils.js +80 -0
- package/dist/cli/backend/src/utils/async.utils.js.map +1 -0
- package/dist/cli/backend/src/utils/native-binding.utils.d.ts +128 -0
- package/dist/cli/backend/src/utils/native-binding.utils.d.ts.map +1 -0
- package/dist/cli/backend/src/utils/native-binding.utils.js +206 -0
- package/dist/cli/backend/src/utils/native-binding.utils.js.map +1 -0
- package/dist/cli/backend/src/websocket/chat.gateway.d.ts +142 -0
- package/dist/cli/backend/src/websocket/chat.gateway.d.ts.map +1 -0
- package/dist/cli/backend/src/websocket/chat.gateway.js +327 -0
- package/dist/cli/backend/src/websocket/chat.gateway.js.map +1 -0
- package/dist/cli/backend/src/websocket/terminal.gateway.d.ts +341 -0
- package/dist/cli/backend/src/websocket/terminal.gateway.d.ts.map +1 -0
- package/dist/cli/backend/src/websocket/terminal.gateway.js +933 -0
- package/dist/cli/backend/src/websocket/terminal.gateway.js.map +1 -0
- package/dist/cli/config/vitest-exclude.constants.d.ts +53 -0
- package/dist/cli/config/vitest-exclude.constants.d.ts.map +1 -0
- package/dist/cli/config/vitest-exclude.constants.js +55 -0
- package/dist/cli/config/vitest-exclude.constants.js.map +1 -0
- package/package.json +2 -2
- package/packages/crewly-agent/src/cli.ts +16 -6
- package/packages/crewly-agent/src/runtime/agent-runner.service.test.ts +311 -33
- package/packages/crewly-agent/src/runtime/agent-runner.service.ts +221 -12
- package/packages/crewly-agent/src/runtime/tool-registry.test.ts +19 -0
- package/packages/crewly-agent/src/runtime/tool-registry.ts +4 -1
- package/packages/crewly-agent/src/runtime/types.ts +65 -5
|
@@ -0,0 +1,1094 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack Service
|
|
3
|
+
*
|
|
4
|
+
* Manages Slack bot connection and messaging using Bolt SDK.
|
|
5
|
+
* Enables bidirectional communication between Slack and Crewly.
|
|
6
|
+
*
|
|
7
|
+
* @module services/slack
|
|
8
|
+
*/
|
|
9
|
+
import { EventEmitter } from 'events';
|
|
10
|
+
import { createReadStream } from 'fs';
|
|
11
|
+
import { basename } from 'path';
|
|
12
|
+
import { isUserAllowed } from '../../types/slack.types.js';
|
|
13
|
+
import { CROSS_MACHINE_PREFIX } from '../../types/cross-machine.types.js';
|
|
14
|
+
import { SLACK_IMAGE_CONSTANTS, SLACK_FILE_UPLOAD_CONSTANTS, SLACK_DEDUP_CONSTANTS, SLACK_RECONNECT_CONSTANTS, ORCHESTRATOR_SESSION_NAME } from '../../constants.js';
|
|
15
|
+
import { LoggerService } from '../core/logger.service.js';
|
|
16
|
+
import { ContentApprovalService } from '../onboarding/content-approval.service.js';
|
|
17
|
+
import { getAgentBehaviorLogService } from '../observability/agent-behavior-log.singleton.js';
|
|
18
|
+
/**
|
|
19
|
+
* Slack Service singleton instance
|
|
20
|
+
*/
|
|
21
|
+
let slackServiceInstance = null;
|
|
22
|
+
/**
|
|
23
|
+
* SlackService class for managing Slack bot operations
|
|
24
|
+
*/
|
|
25
|
+
export class SlackService extends EventEmitter {
|
|
26
|
+
logger = LoggerService.getInstance().createComponentLogger('SlackService');
|
|
27
|
+
app = null;
|
|
28
|
+
client = null;
|
|
29
|
+
config = null;
|
|
30
|
+
status = {
|
|
31
|
+
connected: false,
|
|
32
|
+
socketMode: false,
|
|
33
|
+
messagesSent: 0,
|
|
34
|
+
messagesReceived: 0,
|
|
35
|
+
};
|
|
36
|
+
conversationContexts = new Map();
|
|
37
|
+
/** Cached Bolt App constructor from the first successful dynamic import */
|
|
38
|
+
cachedAppConstructor = null;
|
|
39
|
+
/** Cached Bolt LogLevel enum from the first successful dynamic import */
|
|
40
|
+
cachedLogLevelEnum = null;
|
|
41
|
+
/**
|
|
42
|
+
* Message deduplication tracker.
|
|
43
|
+
* Maps a fingerprint (channelId:threadTs:textHash) to the timestamp it was sent.
|
|
44
|
+
* Prevents identical messages from being sent to the same thread within a time window.
|
|
45
|
+
*/
|
|
46
|
+
recentMessageFingerprints = new Map();
|
|
47
|
+
/** Whether a reconnection attempt is currently in progress */
|
|
48
|
+
reconnecting = false;
|
|
49
|
+
/** Timer handle for the reconnection grace period */
|
|
50
|
+
reconnectGraceTimer = null;
|
|
51
|
+
/** Timer handle for the periodic health check */
|
|
52
|
+
healthCheckTimer = null;
|
|
53
|
+
/** Current consecutive reconnection attempt count */
|
|
54
|
+
reconnectAttempts = 0;
|
|
55
|
+
/** Whether the service has been intentionally shut down (skip auto-reconnect) */
|
|
56
|
+
intentionalDisconnect = false;
|
|
57
|
+
/** Timestamp of the last successful Slack API ping */
|
|
58
|
+
lastPingAt = 0;
|
|
59
|
+
/** Number of consecutive health-check ping failures */
|
|
60
|
+
consecutivePingFailures = 0;
|
|
61
|
+
/**
|
|
62
|
+
* Initialize the Slack service with configuration
|
|
63
|
+
*
|
|
64
|
+
* @param config - Slack bot configuration
|
|
65
|
+
* @returns Promise that resolves when connected
|
|
66
|
+
*/
|
|
67
|
+
async initialize(config) {
|
|
68
|
+
this.config = config;
|
|
69
|
+
this.intentionalDisconnect = false;
|
|
70
|
+
this.reconnectAttempts = 0;
|
|
71
|
+
try {
|
|
72
|
+
// Dynamic import of @slack/bolt (CJS module requires default import handling).
|
|
73
|
+
// Cache the constructor and LogLevel on first import for reuse in reconnect.
|
|
74
|
+
const boltModule = await import('@slack/bolt');
|
|
75
|
+
const defaultExport = boltModule.default;
|
|
76
|
+
const App = (boltModule.App ?? defaultExport?.App);
|
|
77
|
+
const LogLevel = (boltModule.LogLevel ?? defaultExport?.LogLevel);
|
|
78
|
+
this.cachedAppConstructor = App;
|
|
79
|
+
this.cachedLogLevelEnum = LogLevel ?? null;
|
|
80
|
+
this.app = new App({
|
|
81
|
+
token: config.botToken,
|
|
82
|
+
appToken: config.appToken,
|
|
83
|
+
signingSecret: config.signingSecret,
|
|
84
|
+
socketMode: config.socketMode,
|
|
85
|
+
logLevel: LogLevel?.INFO ?? 'info',
|
|
86
|
+
});
|
|
87
|
+
this.client = this.app.client;
|
|
88
|
+
// Set up event handlers
|
|
89
|
+
this.setupEventHandlers();
|
|
90
|
+
// Start the app
|
|
91
|
+
if (config.socketMode) {
|
|
92
|
+
await this.app.start();
|
|
93
|
+
this.status.connected = true;
|
|
94
|
+
this.status.socketMode = true;
|
|
95
|
+
// Monitor SocketModeClient connection state for logging and status tracking
|
|
96
|
+
this.setupConnectionMonitoring();
|
|
97
|
+
this.emit('connected');
|
|
98
|
+
this.logger.info('Connected in Socket Mode');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
// Ensure partially initialized Bolt app is torn down to avoid
|
|
103
|
+
// lingering socket retries/background tasks after failed startup.
|
|
104
|
+
if (this.app) {
|
|
105
|
+
try {
|
|
106
|
+
await this.app.stop();
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// Ignore shutdown errors during failed initialization
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
this.app = null;
|
|
113
|
+
this.client = null;
|
|
114
|
+
this.status.connected = false;
|
|
115
|
+
this.status.socketMode = false;
|
|
116
|
+
this.status.lastError = error.message;
|
|
117
|
+
this.status.lastErrorAt = new Date().toISOString();
|
|
118
|
+
this.emit('error', error);
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Set up Slack event handlers
|
|
124
|
+
*/
|
|
125
|
+
setupEventHandlers() {
|
|
126
|
+
if (!this.app || !this.config)
|
|
127
|
+
return;
|
|
128
|
+
const config = this.config;
|
|
129
|
+
// Handle direct messages
|
|
130
|
+
this.app.message(async ({ message }) => {
|
|
131
|
+
// Allow messages with text or files (or both)
|
|
132
|
+
if (!message.text && (!message.files || message.files.length === 0))
|
|
133
|
+
return;
|
|
134
|
+
if (!message.user)
|
|
135
|
+
return;
|
|
136
|
+
// Bypass user permission check for cross-machine messages —
|
|
137
|
+
// these come from other bots and are authenticated by device ID,
|
|
138
|
+
// not Slack user ID. The CrossMachineMessageService handles its
|
|
139
|
+
// own security (device identity, target filtering, deduplication).
|
|
140
|
+
const isCrossMachine = message.text && message.text.startsWith(CROSS_MACHINE_PREFIX);
|
|
141
|
+
// Check user permissions (skip for cross-machine messages)
|
|
142
|
+
if (!isCrossMachine && !isUserAllowed(message.user, config)) {
|
|
143
|
+
this.logger.info('Unauthorized user', { userId: message.user });
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
// Pass ALL files through; downstream services handle image vs non-image distinction
|
|
147
|
+
const allFiles = (message.files || []);
|
|
148
|
+
const imageFiles = allFiles.filter(f => f.mimetype?.startsWith('image/'));
|
|
149
|
+
const incomingMessage = {
|
|
150
|
+
id: message.ts || '',
|
|
151
|
+
type: 'message',
|
|
152
|
+
text: message.text || '',
|
|
153
|
+
userId: message.user,
|
|
154
|
+
channelId: message.channel,
|
|
155
|
+
threadTs: message.thread_ts,
|
|
156
|
+
ts: message.ts || '',
|
|
157
|
+
teamId: message.team || '',
|
|
158
|
+
eventTs: message.ts || '',
|
|
159
|
+
files: allFiles.length > 0 ? allFiles : undefined,
|
|
160
|
+
hasImages: imageFiles.length > 0,
|
|
161
|
+
hasFiles: allFiles.length > 0,
|
|
162
|
+
};
|
|
163
|
+
this.status.messagesReceived++;
|
|
164
|
+
this.status.lastEventAt = new Date().toISOString();
|
|
165
|
+
this.emit('message', incomingMessage);
|
|
166
|
+
});
|
|
167
|
+
// Handle @mentions
|
|
168
|
+
this.app.event('app_mention', async ({ event }) => {
|
|
169
|
+
if (!isUserAllowed(event.user, config)) {
|
|
170
|
+
this.logger.info('Unauthorized user mention', { userId: event.user });
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const incomingMessage = {
|
|
174
|
+
id: event.ts,
|
|
175
|
+
type: 'app_mention',
|
|
176
|
+
text: event.text,
|
|
177
|
+
userId: event.user,
|
|
178
|
+
channelId: event.channel,
|
|
179
|
+
threadTs: event.thread_ts,
|
|
180
|
+
ts: event.ts,
|
|
181
|
+
teamId: event.team || '',
|
|
182
|
+
eventTs: event.event_ts,
|
|
183
|
+
};
|
|
184
|
+
this.status.messagesReceived++;
|
|
185
|
+
this.status.lastEventAt = new Date().toISOString();
|
|
186
|
+
this.emit('message', incomingMessage);
|
|
187
|
+
});
|
|
188
|
+
// Handle content approval button clicks (Block Kit interactive actions)
|
|
189
|
+
this.app.action(/content_approval_(approve|reject)/, async ({ action, body, ack, respond }) => {
|
|
190
|
+
await ack();
|
|
191
|
+
const approvalId = action.value;
|
|
192
|
+
const actionType = action.action_id;
|
|
193
|
+
const slackUserName = body.user.name || body.user.username || body.user.id;
|
|
194
|
+
const channelId = body.channel?.id || body.container?.channel_id;
|
|
195
|
+
const messageTs = body.message?.ts || body.container?.message_ts;
|
|
196
|
+
if (!approvalId) {
|
|
197
|
+
await respond({ text: 'Error: Missing approval ID.', replace_original: false, response_type: 'ephemeral' });
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
const service = ContentApprovalService.getInstance();
|
|
202
|
+
const existing = service.get(approvalId);
|
|
203
|
+
if (!existing) {
|
|
204
|
+
await respond({ text: `Approval \`${approvalId.slice(0, 8)}\` not found.`, replace_original: false, response_type: 'ephemeral' });
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (existing.status !== 'pending') {
|
|
208
|
+
await respond({
|
|
209
|
+
text: `This approval has already been ${existing.status} by ${existing.resolvedBy ?? 'someone'}.`,
|
|
210
|
+
replace_original: false,
|
|
211
|
+
response_type: 'ephemeral',
|
|
212
|
+
});
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const isApprove = actionType === 'content_approval_approve';
|
|
216
|
+
const result = isApprove
|
|
217
|
+
? service.approve(approvalId, slackUserName)
|
|
218
|
+
: service.reject(approvalId, slackUserName);
|
|
219
|
+
if (!result) {
|
|
220
|
+
await respond({ text: 'Failed to resolve approval.', replace_original: false, response_type: 'ephemeral' });
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
// Update the original message to replace buttons with status
|
|
224
|
+
const statusEmoji = isApprove ? '\u2705' : '\u274c';
|
|
225
|
+
const statusText = isApprove ? 'Approved' : 'Rejected';
|
|
226
|
+
const updatedText = `${statusEmoji} ${statusText} by @${slackUserName}`;
|
|
227
|
+
if (channelId && messageTs) {
|
|
228
|
+
const updatedBlocks = [
|
|
229
|
+
{
|
|
230
|
+
type: 'section',
|
|
231
|
+
text: { type: 'mrkdwn', text: `~${existing.content.slice(0, 200)}~` },
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
type: 'context',
|
|
235
|
+
elements: [
|
|
236
|
+
{ type: 'mrkdwn', text: `${updatedText} \u2022 ${new Date().toLocaleString()}` },
|
|
237
|
+
],
|
|
238
|
+
},
|
|
239
|
+
];
|
|
240
|
+
await this.updateMessage(channelId, messageTs, updatedText, updatedBlocks);
|
|
241
|
+
}
|
|
242
|
+
this.emit('content_approval_resolved', {
|
|
243
|
+
approvalId,
|
|
244
|
+
action: isApprove ? 'approved' : 'rejected',
|
|
245
|
+
resolvedBy: slackUserName,
|
|
246
|
+
});
|
|
247
|
+
this.logger.info('Content approval resolved via Slack button', {
|
|
248
|
+
approvalId: approvalId.slice(0, 8),
|
|
249
|
+
action: statusText.toLowerCase(),
|
|
250
|
+
resolvedBy: slackUserName,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
catch (err) {
|
|
254
|
+
this.logger.error('Content approval action handler error', {
|
|
255
|
+
approvalId: approvalId?.slice(0, 8),
|
|
256
|
+
error: err instanceof Error ? err.message : String(err),
|
|
257
|
+
});
|
|
258
|
+
await respond({
|
|
259
|
+
text: `Error processing approval: ${err instanceof Error ? err.message : 'Unknown error'}`,
|
|
260
|
+
replace_original: false,
|
|
261
|
+
response_type: 'ephemeral',
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
// Handle errors
|
|
266
|
+
this.app.error(async (error) => {
|
|
267
|
+
this.logger.error('Error', { error: error.message });
|
|
268
|
+
this.status.lastError = error.message;
|
|
269
|
+
this.status.lastErrorAt = new Date().toISOString();
|
|
270
|
+
this.emit('error', error);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Attach listeners to the underlying SocketModeClient to track
|
|
275
|
+
* connection lifecycle (disconnected, reconnecting, connected).
|
|
276
|
+
* Updates `status.connected` so health checks reflect actual state.
|
|
277
|
+
*
|
|
278
|
+
* The Bolt App stores the receiver at `app.receiver`, and the
|
|
279
|
+
* SocketModeReceiver exposes `client` (SocketModeClient), which
|
|
280
|
+
* is an EventEmitter emitting: connected, reconnecting, disconnected, close.
|
|
281
|
+
*/
|
|
282
|
+
setupConnectionMonitoring() {
|
|
283
|
+
// Access the SocketModeClient from Bolt's internal receiver.
|
|
284
|
+
// This relies on Bolt's internal structure, so we guard with try/catch.
|
|
285
|
+
try {
|
|
286
|
+
const receiver = this.app?.receiver;
|
|
287
|
+
const socketClient = receiver?.client;
|
|
288
|
+
if (!socketClient) {
|
|
289
|
+
this.logger.warn('Could not access SocketModeClient for connection monitoring');
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
// Issue #548 — `@slack/socket-mode`'s finity state machine throws
|
|
293
|
+
// `Unhandled event 'server explicit disconnect' in state 'connecting'`
|
|
294
|
+
// when Slack sends a disconnect signal during the reconnect
|
|
295
|
+
// handshake window. The throw is SYNCHRONOUS inside the WebSocket
|
|
296
|
+
// message callback (WebSocket.onMessage → SocketModeClient.
|
|
297
|
+
// onWebSocketMessage → finity.handle → throw) so it surfaces as
|
|
298
|
+
// an uncaughtException that takes the process down on v1 hosts
|
|
299
|
+
// (no pm2/systemd to restart). CrewlyNode1 went dark for ~21
|
|
300
|
+
// hours on 2026-05-14 from exactly this.
|
|
301
|
+
//
|
|
302
|
+
// Wrap `onWebSocketMessage` with a synchronous try/catch that
|
|
303
|
+
// swallows the unhandled-event throw, logs it as a recoverable
|
|
304
|
+
// event, and triggers the reconnect path. Other throws still
|
|
305
|
+
// surface normally — we only suppress the specific finity
|
|
306
|
+
// "Unhandled event" signature.
|
|
307
|
+
const wrappedClient = socketClient;
|
|
308
|
+
const originalOnMessage = wrappedClient.onWebSocketMessage;
|
|
309
|
+
if (typeof originalOnMessage === 'function') {
|
|
310
|
+
wrappedClient.onWebSocketMessage = (...args) => {
|
|
311
|
+
try {
|
|
312
|
+
return originalOnMessage.apply(socketClient, args);
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
316
|
+
if (/Unhandled event/.test(msg)) {
|
|
317
|
+
this.logger.warn('Suppressed finity unhandled-event from SocketModeClient (issue #548) — triggering reconnect', {
|
|
318
|
+
error: msg,
|
|
319
|
+
});
|
|
320
|
+
this.status.connected = false;
|
|
321
|
+
this.status.lastError = `SocketMode finity throw: ${msg}`;
|
|
322
|
+
this.status.lastErrorAt = new Date().toISOString();
|
|
323
|
+
this.scheduleReconnect();
|
|
324
|
+
return undefined;
|
|
325
|
+
}
|
|
326
|
+
throw err;
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
this.logger.info('SocketModeClient.onWebSocketMessage wrapped for #548 finity-throw recovery');
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
this.logger.warn('SocketModeClient.onWebSocketMessage not found — #548 finity-throw guard NOT installed');
|
|
333
|
+
}
|
|
334
|
+
socketClient.on('disconnected', () => {
|
|
335
|
+
this.status.connected = false;
|
|
336
|
+
this.status.lastError = 'Socket Mode connection lost';
|
|
337
|
+
this.status.lastErrorAt = new Date().toISOString();
|
|
338
|
+
this.logger.warn('Socket Mode disconnected');
|
|
339
|
+
this.emit('disconnected');
|
|
340
|
+
this.scheduleReconnect();
|
|
341
|
+
});
|
|
342
|
+
socketClient.on('reconnecting', () => {
|
|
343
|
+
this.status.connected = false;
|
|
344
|
+
this.logger.info('Socket Mode reconnecting (Bolt built-in)...');
|
|
345
|
+
});
|
|
346
|
+
socketClient.on('connected', () => {
|
|
347
|
+
this.status.connected = true;
|
|
348
|
+
this.reconnectAttempts = 0;
|
|
349
|
+
this.consecutivePingFailures = 0;
|
|
350
|
+
this.lastPingAt = Date.now();
|
|
351
|
+
this.cancelReconnectGrace();
|
|
352
|
+
this.logger.info('Socket Mode reconnected');
|
|
353
|
+
this.emit('connected');
|
|
354
|
+
});
|
|
355
|
+
socketClient.on('close', () => {
|
|
356
|
+
// 'close' fires on WebSocket close before reconnection kicks in
|
|
357
|
+
if (this.status.connected) {
|
|
358
|
+
this.status.connected = false;
|
|
359
|
+
this.logger.warn('Socket Mode WebSocket closed, awaiting reconnect...');
|
|
360
|
+
this.scheduleReconnect();
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
catch (err) {
|
|
365
|
+
this.logger.warn('Failed to set up connection monitoring', {
|
|
366
|
+
error: err instanceof Error ? err.message : String(err),
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
// Start periodic health check
|
|
370
|
+
this.startHealthCheck();
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Start a periodic health check that verifies the Socket Mode connection
|
|
374
|
+
* is alive and triggers reconnection if it has silently died.
|
|
375
|
+
*
|
|
376
|
+
* Uses an active Slack API ping (auth.test) to detect half-open sockets
|
|
377
|
+
* that appear connected but are actually dead after WiFi/network drops.
|
|
378
|
+
*/
|
|
379
|
+
startHealthCheck() {
|
|
380
|
+
this.stopHealthCheck();
|
|
381
|
+
this.lastPingAt = Date.now();
|
|
382
|
+
this.consecutivePingFailures = 0;
|
|
383
|
+
this.healthCheckTimer = setInterval(async () => {
|
|
384
|
+
if (this.intentionalDisconnect)
|
|
385
|
+
return;
|
|
386
|
+
// Case 1: Already known to be disconnected — trigger reconnect
|
|
387
|
+
if (!this.status.connected && !this.reconnecting) {
|
|
388
|
+
this.logger.warn('Health check: Socket Mode not connected and no reconnect in progress — triggering reconnect');
|
|
389
|
+
this.attemptReconnect();
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
// Case 2: Status says connected — verify with an active API ping
|
|
393
|
+
if (this.status.connected && this.client && !this.reconnecting) {
|
|
394
|
+
try {
|
|
395
|
+
const pingPromise = this.client.auth.test();
|
|
396
|
+
const timeoutMs = SLACK_RECONNECT_CONSTANTS.PING_TIMEOUT_MS;
|
|
397
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
398
|
+
const timer = setTimeout(() => reject(new Error('Ping timeout')), timeoutMs);
|
|
399
|
+
// Clear timer if ping resolves first to prevent leak
|
|
400
|
+
pingPromise.then(() => clearTimeout(timer), () => clearTimeout(timer));
|
|
401
|
+
});
|
|
402
|
+
await Promise.race([pingPromise, timeoutPromise]);
|
|
403
|
+
// Ping succeeded — connection is truly alive
|
|
404
|
+
this.lastPingAt = Date.now();
|
|
405
|
+
this.consecutivePingFailures = 0;
|
|
406
|
+
}
|
|
407
|
+
catch (err) {
|
|
408
|
+
this.consecutivePingFailures++;
|
|
409
|
+
this.logger.warn('Health check ping failed', {
|
|
410
|
+
consecutiveFailures: this.consecutivePingFailures,
|
|
411
|
+
error: err instanceof Error ? err.message : String(err),
|
|
412
|
+
});
|
|
413
|
+
// After consecutive failures, declare connection dead and reconnect
|
|
414
|
+
if (this.consecutivePingFailures >= SLACK_RECONNECT_CONSTANTS.PING_FAILURES_BEFORE_RECONNECT) {
|
|
415
|
+
this.logger.warn('Health check: connection appears dead after consecutive ping failures — forcing reconnect', {
|
|
416
|
+
consecutiveFailures: this.consecutivePingFailures,
|
|
417
|
+
lastPingAt: new Date(this.lastPingAt).toISOString(),
|
|
418
|
+
});
|
|
419
|
+
this.status.connected = false;
|
|
420
|
+
this.consecutivePingFailures = 0;
|
|
421
|
+
this.attemptReconnect();
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}, SLACK_RECONNECT_CONSTANTS.HEALTH_CHECK_INTERVAL_MS);
|
|
426
|
+
// Allow Node to exit even if health check timer is still active
|
|
427
|
+
if (this.healthCheckTimer && typeof this.healthCheckTimer === 'object' && 'unref' in this.healthCheckTimer) {
|
|
428
|
+
this.healthCheckTimer.unref();
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Stop the periodic health check timer.
|
|
433
|
+
*/
|
|
434
|
+
stopHealthCheck() {
|
|
435
|
+
if (this.healthCheckTimer) {
|
|
436
|
+
clearInterval(this.healthCheckTimer);
|
|
437
|
+
this.healthCheckTimer = null;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Schedule a reconnection attempt after a grace period.
|
|
442
|
+
* The grace period gives Bolt's built-in reconnect a chance to recover first.
|
|
443
|
+
* If the connection comes back within the grace period, the reconnect is cancelled.
|
|
444
|
+
*/
|
|
445
|
+
scheduleReconnect() {
|
|
446
|
+
if (this.intentionalDisconnect || this.reconnecting || this.reconnectGraceTimer)
|
|
447
|
+
return;
|
|
448
|
+
this.logger.info('Scheduling reconnect after grace period', {
|
|
449
|
+
gracePeriodMs: SLACK_RECONNECT_CONSTANTS.GRACE_PERIOD_MS,
|
|
450
|
+
});
|
|
451
|
+
this.reconnectGraceTimer = setTimeout(() => {
|
|
452
|
+
this.reconnectGraceTimer = null;
|
|
453
|
+
if (!this.status.connected && !this.intentionalDisconnect) {
|
|
454
|
+
this.logger.warn('Grace period expired, Bolt reconnect did not recover — starting manual reconnect');
|
|
455
|
+
this.attemptReconnect();
|
|
456
|
+
}
|
|
457
|
+
}, SLACK_RECONNECT_CONSTANTS.GRACE_PERIOD_MS);
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Cancel the reconnection grace period timer.
|
|
461
|
+
*/
|
|
462
|
+
cancelReconnectGrace() {
|
|
463
|
+
if (this.reconnectGraceTimer) {
|
|
464
|
+
clearTimeout(this.reconnectGraceTimer);
|
|
465
|
+
this.reconnectGraceTimer = null;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Attempt to reconnect by tearing down the current Bolt app and
|
|
470
|
+
* re-initializing from scratch with the saved config.
|
|
471
|
+
* Uses exponential backoff between attempts.
|
|
472
|
+
*/
|
|
473
|
+
async attemptReconnect() {
|
|
474
|
+
if (this.reconnecting || this.intentionalDisconnect || !this.config)
|
|
475
|
+
return;
|
|
476
|
+
const maxAttempts = SLACK_RECONNECT_CONSTANTS.MAX_ATTEMPTS;
|
|
477
|
+
if (maxAttempts > 0 && this.reconnectAttempts >= maxAttempts) {
|
|
478
|
+
this.logger.error('Max reconnection attempts reached, giving up', {
|
|
479
|
+
attempts: this.reconnectAttempts,
|
|
480
|
+
maxAttempts,
|
|
481
|
+
});
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
this.reconnecting = true;
|
|
485
|
+
this.reconnectAttempts++;
|
|
486
|
+
const delay = Math.min(SLACK_RECONNECT_CONSTANTS.INITIAL_DELAY_MS * Math.pow(SLACK_RECONNECT_CONSTANTS.BACKOFF_MULTIPLIER, this.reconnectAttempts - 1), SLACK_RECONNECT_CONSTANTS.MAX_DELAY_MS);
|
|
487
|
+
this.logger.info('Reconnecting to Slack Socket Mode', {
|
|
488
|
+
attempt: this.reconnectAttempts,
|
|
489
|
+
delayMs: delay,
|
|
490
|
+
});
|
|
491
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
492
|
+
// Bail out if we reconnected or intentionally disconnected while waiting
|
|
493
|
+
if (this.status.connected || this.intentionalDisconnect) {
|
|
494
|
+
this.reconnecting = false;
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
try {
|
|
498
|
+
// Guard: cached App constructor must be available from initialize()
|
|
499
|
+
if (!this.cachedAppConstructor) {
|
|
500
|
+
throw new Error('App constructor not cached — initialize() must be called before reconnect');
|
|
501
|
+
}
|
|
502
|
+
// Tear down existing app
|
|
503
|
+
if (this.app) {
|
|
504
|
+
try {
|
|
505
|
+
await this.app.stop();
|
|
506
|
+
}
|
|
507
|
+
catch {
|
|
508
|
+
// Ignore teardown errors
|
|
509
|
+
}
|
|
510
|
+
this.app = null;
|
|
511
|
+
this.client = null;
|
|
512
|
+
}
|
|
513
|
+
// Re-initialize using cached constructor (avoids stale dynamic import issues)
|
|
514
|
+
const App = this.cachedAppConstructor;
|
|
515
|
+
const LogLevel = this.cachedLogLevelEnum;
|
|
516
|
+
this.app = new App({
|
|
517
|
+
token: this.config.botToken,
|
|
518
|
+
appToken: this.config.appToken,
|
|
519
|
+
signingSecret: this.config.signingSecret,
|
|
520
|
+
socketMode: this.config.socketMode,
|
|
521
|
+
logLevel: LogLevel?.INFO ?? 'info',
|
|
522
|
+
});
|
|
523
|
+
this.client = this.app.client;
|
|
524
|
+
this.setupEventHandlers();
|
|
525
|
+
await this.app.start();
|
|
526
|
+
this.status.connected = true;
|
|
527
|
+
this.status.socketMode = true;
|
|
528
|
+
this.reconnectAttempts = 0;
|
|
529
|
+
this.reconnecting = false;
|
|
530
|
+
this.consecutivePingFailures = 0;
|
|
531
|
+
this.lastPingAt = Date.now();
|
|
532
|
+
this.setupConnectionMonitoring();
|
|
533
|
+
this.emit('connected');
|
|
534
|
+
this.logger.info('Successfully reconnected to Slack Socket Mode');
|
|
535
|
+
}
|
|
536
|
+
catch (error) {
|
|
537
|
+
this.reconnecting = false;
|
|
538
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
539
|
+
this.status.lastError = `Reconnect failed: ${errorMessage}`;
|
|
540
|
+
this.status.lastErrorAt = new Date().toISOString();
|
|
541
|
+
// Classify error: fatal errors stop the loop, transient errors retry
|
|
542
|
+
if (this.isFatalReconnectError(error)) {
|
|
543
|
+
this.logger.error('Fatal reconnection error — stopping retry loop', {
|
|
544
|
+
attempt: this.reconnectAttempts,
|
|
545
|
+
error: errorMessage,
|
|
546
|
+
});
|
|
547
|
+
this.emit('error', error instanceof Error ? error : new Error(errorMessage));
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
this.logger.error('Transient reconnection error — will retry', {
|
|
551
|
+
attempt: this.reconnectAttempts,
|
|
552
|
+
error: errorMessage,
|
|
553
|
+
});
|
|
554
|
+
// Schedule next attempt — the health check will also retry if this doesn't fire
|
|
555
|
+
if (!this.intentionalDisconnect) {
|
|
556
|
+
this.scheduleReconnect();
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Classify whether a reconnection error is fatal (non-recoverable)
|
|
562
|
+
* or transient (may succeed on retry).
|
|
563
|
+
*
|
|
564
|
+
* Fatal errors: missing constructor, invalid config, auth errors.
|
|
565
|
+
* Transient errors: network timeouts, temporary server errors.
|
|
566
|
+
*
|
|
567
|
+
* @param error - The error caught during reconnection
|
|
568
|
+
* @returns True if the error is fatal and retrying would be futile
|
|
569
|
+
*/
|
|
570
|
+
isFatalReconnectError(error) {
|
|
571
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
572
|
+
const lowerMessage = message.toLowerCase();
|
|
573
|
+
/** Patterns that indicate a non-recoverable configuration or code error */
|
|
574
|
+
const fatalPatterns = [
|
|
575
|
+
'not a constructor',
|
|
576
|
+
'is not a function',
|
|
577
|
+
'constructor not cached',
|
|
578
|
+
'invalid_auth',
|
|
579
|
+
'token_revoked',
|
|
580
|
+
'account_inactive',
|
|
581
|
+
'missing required',
|
|
582
|
+
'config',
|
|
583
|
+
];
|
|
584
|
+
return fatalPatterns.some(pattern => lowerMessage.includes(pattern));
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Send a message to Slack with content-based deduplication.
|
|
588
|
+
*
|
|
589
|
+
* Generates a fingerprint from (channelId, threadTs, text) and suppresses
|
|
590
|
+
* duplicate sends within a configurable time window (default 30s).
|
|
591
|
+
* This is the single chokepoint for all Slack message sends — skill delivery,
|
|
592
|
+
* bridge fallback, and direct API calls all converge here.
|
|
593
|
+
*
|
|
594
|
+
* @param message - Message to send
|
|
595
|
+
* @returns Promise with message timestamp (empty string if deduplicated)
|
|
596
|
+
*/
|
|
597
|
+
async sendMessage(message) {
|
|
598
|
+
if (!this.client) {
|
|
599
|
+
throw new Error('Slack client not initialized');
|
|
600
|
+
}
|
|
601
|
+
// Content-based deduplication: suppress identical messages within time window
|
|
602
|
+
const fingerprint = this.buildMessageFingerprint(message);
|
|
603
|
+
const now = Date.now();
|
|
604
|
+
const lastSentAt = this.recentMessageFingerprints.get(fingerprint);
|
|
605
|
+
if (lastSentAt !== undefined && (now - lastSentAt) < SLACK_DEDUP_CONSTANTS.DEDUP_WINDOW_MS) {
|
|
606
|
+
this.logger.info('Slack message deduplicated — identical message sent recently', {
|
|
607
|
+
channelId: message.channelId,
|
|
608
|
+
threadTs: message.threadTs,
|
|
609
|
+
ageMs: now - lastSentAt,
|
|
610
|
+
});
|
|
611
|
+
return '';
|
|
612
|
+
}
|
|
613
|
+
try {
|
|
614
|
+
const result = await this.client.chat.postMessage({
|
|
615
|
+
channel: message.channelId,
|
|
616
|
+
text: message.text,
|
|
617
|
+
thread_ts: message.threadTs,
|
|
618
|
+
blocks: message.blocks,
|
|
619
|
+
attachments: message.attachments,
|
|
620
|
+
unfurl_links: message.unfurlLinks,
|
|
621
|
+
unfurl_media: message.unfurlMedia,
|
|
622
|
+
});
|
|
623
|
+
this.status.messagesSent++;
|
|
624
|
+
// Track this fingerprint for future dedup
|
|
625
|
+
this.trackMessageFingerprint(fingerprint, now);
|
|
626
|
+
// Mirror the outbound reply into chat-v2 so the Slack thread shows both
|
|
627
|
+
// sides ("收和发") in the consolidated chat. Best-effort, thread-only.
|
|
628
|
+
if (message.threadTs) {
|
|
629
|
+
void this.recordOutboundToChatV2(message);
|
|
630
|
+
}
|
|
631
|
+
return result.ts || '';
|
|
632
|
+
}
|
|
633
|
+
catch (error) {
|
|
634
|
+
this.logger.error('Send message error', { error: error instanceof Error ? error.message : String(error) });
|
|
635
|
+
// F14: record slack.delivery.failed event (best-effort — never
|
|
636
|
+
// changes retry semantics or throws). The wrapper swallows
|
|
637
|
+
// construction errors and `record()` itself swallows DB errors.
|
|
638
|
+
try {
|
|
639
|
+
const errMessage = error instanceof Error ? error.message : String(error);
|
|
640
|
+
getAgentBehaviorLogService()?.record({
|
|
641
|
+
type: 'slack.delivery.failed',
|
|
642
|
+
agent: '',
|
|
643
|
+
thread: `${message.channelId}:${message.threadTs ?? ''}`,
|
|
644
|
+
reason: 'api_error',
|
|
645
|
+
details: {
|
|
646
|
+
errorMessage: errMessage,
|
|
647
|
+
textLength: (message.text ?? '').length,
|
|
648
|
+
hasBlocks: Array.isArray(message.blocks) && message.blocks.length > 0,
|
|
649
|
+
},
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
catch {
|
|
653
|
+
/* observability is best-effort — never block the original throw */
|
|
654
|
+
}
|
|
655
|
+
throw error;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Mirror an outbound Slack reply into the canonical chat-v2 store so the
|
|
660
|
+
* Slack thread shows both inbound (user) and outbound (agent) messages in
|
|
661
|
+
* the consolidated /team-chat surface. Records against the SAME channel the
|
|
662
|
+
* inbound persisted to — `synthesizeSlackConversationId(channelId, threadTs)`.
|
|
663
|
+
*
|
|
664
|
+
* Best-effort: dynamic imports + try/catch so a chat-v2 failure never
|
|
665
|
+
* affects Slack delivery. Only called for threaded replies (threadTs set).
|
|
666
|
+
*
|
|
667
|
+
* @param message - The outbound Slack message that was just sent.
|
|
668
|
+
*/
|
|
669
|
+
async recordOutboundToChatV2(message) {
|
|
670
|
+
try {
|
|
671
|
+
if (!message.channelId || !message.threadTs || !(message.text ?? '').trim())
|
|
672
|
+
return;
|
|
673
|
+
const [{ getChatV2Service }, { synthesizeSlackConversationId, slackOutboundClientMessageId }] = await Promise.all([
|
|
674
|
+
import('../chat-v2/chat-v2.singleton.js'),
|
|
675
|
+
import('../chat-v2/legacy-dto.utils.js'),
|
|
676
|
+
]);
|
|
677
|
+
const chatV2 = getChatV2Service();
|
|
678
|
+
const conversationId = synthesizeSlackConversationId(message.channelId, message.threadTs);
|
|
679
|
+
const channel = chatV2.ensureChannelForLegacyConversation({
|
|
680
|
+
conversationId,
|
|
681
|
+
agentSession: ORCHESTRATOR_SESSION_NAME,
|
|
682
|
+
});
|
|
683
|
+
chatV2.recordTurn({
|
|
684
|
+
channelId: channel.id,
|
|
685
|
+
senderType: 'agent',
|
|
686
|
+
senderId: channel.agentSession || ORCHESTRATOR_SESSION_NAME,
|
|
687
|
+
content: message.text,
|
|
688
|
+
// Shared idempotency key with the /slack/send bookkeeping path so the
|
|
689
|
+
// same reply isn't persisted twice (duplicate bubbles in the merged
|
|
690
|
+
// Orchestrator timeline).
|
|
691
|
+
clientMessageId: slackOutboundClientMessageId(message.channelId, message.threadTs, message.text),
|
|
692
|
+
metadata: {
|
|
693
|
+
source: 'slack',
|
|
694
|
+
slackChannelId: message.channelId,
|
|
695
|
+
slackThreadTs: message.threadTs,
|
|
696
|
+
},
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
catch (err) {
|
|
700
|
+
this.logger.warn('Failed to mirror outbound Slack message to chat-v2', {
|
|
701
|
+
error: err instanceof Error ? err.message : String(err),
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Build a deduplication fingerprint for a Slack message.
|
|
707
|
+
* Combines channelId, threadTs, and a simple hash of the message text.
|
|
708
|
+
*
|
|
709
|
+
* @param message - Outgoing Slack message
|
|
710
|
+
* @returns Fingerprint string
|
|
711
|
+
*/
|
|
712
|
+
buildMessageFingerprint(message) {
|
|
713
|
+
// Simple string hash (djb2) — fast and sufficient for dedup
|
|
714
|
+
let hash = 5381;
|
|
715
|
+
const text = message.text || '';
|
|
716
|
+
for (let i = 0; i < text.length; i++) {
|
|
717
|
+
hash = ((hash << 5) + hash + text.charCodeAt(i)) | 0;
|
|
718
|
+
}
|
|
719
|
+
return `${message.channelId}:${message.threadTs || ''}:${hash}`;
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Track a message fingerprint and evict stale entries.
|
|
723
|
+
*
|
|
724
|
+
* @param fingerprint - Message fingerprint to track
|
|
725
|
+
* @param timestamp - Current timestamp in ms
|
|
726
|
+
*/
|
|
727
|
+
trackMessageFingerprint(fingerprint, timestamp) {
|
|
728
|
+
this.recentMessageFingerprints.set(fingerprint, timestamp);
|
|
729
|
+
// Evict expired entries and enforce size limit
|
|
730
|
+
if (this.recentMessageFingerprints.size > SLACK_DEDUP_CONSTANTS.MAX_TRACKED_MESSAGES) {
|
|
731
|
+
const windowMs = SLACK_DEDUP_CONSTANTS.DEDUP_WINDOW_MS;
|
|
732
|
+
for (const [key, ts] of this.recentMessageFingerprints) {
|
|
733
|
+
if (timestamp - ts > windowMs) {
|
|
734
|
+
this.recentMessageFingerprints.delete(key);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
// If still over limit after expiry cleanup, drop oldest
|
|
738
|
+
if (this.recentMessageFingerprints.size > SLACK_DEDUP_CONSTANTS.MAX_TRACKED_MESSAGES) {
|
|
739
|
+
const firstKey = this.recentMessageFingerprints.keys().next().value;
|
|
740
|
+
if (firstKey !== undefined) {
|
|
741
|
+
this.recentMessageFingerprints.delete(firstKey);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Send a notification to the default channel
|
|
748
|
+
*
|
|
749
|
+
* @param notification - Notification to send
|
|
750
|
+
*/
|
|
751
|
+
async sendNotification(notification) {
|
|
752
|
+
const targetChannelId = notification.channelId || this.config?.defaultChannelId;
|
|
753
|
+
if (!targetChannelId) {
|
|
754
|
+
this.logger.warn('No channel configured for notification');
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
const blocks = this.formatNotificationBlocks(notification);
|
|
758
|
+
await this.sendMessage({
|
|
759
|
+
channelId: targetChannelId,
|
|
760
|
+
text: `${notification.title}: ${notification.message}`,
|
|
761
|
+
blocks,
|
|
762
|
+
threadTs: notification.threadTs,
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Format notification as Slack blocks
|
|
767
|
+
*
|
|
768
|
+
* @param notification - Notification to format
|
|
769
|
+
* @returns Slack blocks for the notification
|
|
770
|
+
*/
|
|
771
|
+
formatNotificationBlocks(notification) {
|
|
772
|
+
const urgencyEmoji = {
|
|
773
|
+
low: ':white_circle:',
|
|
774
|
+
normal: ':large_blue_circle:',
|
|
775
|
+
high: ':large_orange_circle:',
|
|
776
|
+
critical: ':red_circle:',
|
|
777
|
+
};
|
|
778
|
+
const blocks = [
|
|
779
|
+
{
|
|
780
|
+
type: 'header',
|
|
781
|
+
text: {
|
|
782
|
+
type: 'plain_text',
|
|
783
|
+
text: `${urgencyEmoji[notification.urgency]} ${notification.title}`,
|
|
784
|
+
emoji: true,
|
|
785
|
+
},
|
|
786
|
+
},
|
|
787
|
+
{
|
|
788
|
+
type: 'section',
|
|
789
|
+
text: {
|
|
790
|
+
type: 'mrkdwn',
|
|
791
|
+
text: notification.message,
|
|
792
|
+
},
|
|
793
|
+
},
|
|
794
|
+
];
|
|
795
|
+
// Context block elements are text objects where `text` is a plain string.
|
|
796
|
+
// Using type assertion because SlackElement.text is typed as SlackTextObject
|
|
797
|
+
// but Slack API context elements use text objects directly as elements.
|
|
798
|
+
blocks.push({
|
|
799
|
+
type: 'context',
|
|
800
|
+
elements: [
|
|
801
|
+
{
|
|
802
|
+
type: 'mrkdwn',
|
|
803
|
+
text: `Sent at ${new Date(notification.timestamp).toLocaleString()}`,
|
|
804
|
+
},
|
|
805
|
+
],
|
|
806
|
+
});
|
|
807
|
+
return blocks;
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Update a message
|
|
811
|
+
*
|
|
812
|
+
* @param channelId - Channel ID
|
|
813
|
+
* @param messageTs - Message timestamp
|
|
814
|
+
* @param text - New text
|
|
815
|
+
* @param blocks - Optional new blocks
|
|
816
|
+
*/
|
|
817
|
+
async updateMessage(channelId, messageTs, text, blocks) {
|
|
818
|
+
if (!this.client) {
|
|
819
|
+
throw new Error('Slack client not initialized');
|
|
820
|
+
}
|
|
821
|
+
await this.client.chat.update({
|
|
822
|
+
channel: channelId,
|
|
823
|
+
ts: messageTs,
|
|
824
|
+
text,
|
|
825
|
+
blocks,
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Add a reaction to a message
|
|
830
|
+
*
|
|
831
|
+
* @param channelId - Channel ID
|
|
832
|
+
* @param messageTs - Message timestamp
|
|
833
|
+
* @param emoji - Emoji name (without colons)
|
|
834
|
+
*/
|
|
835
|
+
async addReaction(channelId, messageTs, emoji) {
|
|
836
|
+
if (!this.client) {
|
|
837
|
+
throw new Error('Slack client not initialized');
|
|
838
|
+
}
|
|
839
|
+
await this.client.reactions.add({
|
|
840
|
+
channel: channelId,
|
|
841
|
+
timestamp: messageTs,
|
|
842
|
+
name: emoji,
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Get or create conversation context for a thread
|
|
847
|
+
*
|
|
848
|
+
* @param threadTs - Thread timestamp
|
|
849
|
+
* @param channelId - Channel ID
|
|
850
|
+
* @param userId - User ID
|
|
851
|
+
* @returns Conversation context
|
|
852
|
+
*/
|
|
853
|
+
getConversationContext(threadTs, channelId, userId) {
|
|
854
|
+
const key = `${channelId}:${threadTs}`;
|
|
855
|
+
// NOTE: This is intentionally NOT `synthesizeSlackConversationId`
|
|
856
|
+
// (chat-v2/legacy-dto.utils.ts). That helper produces the chat-v2
|
|
857
|
+
// channel id by replacing the single dot in the Slack ts. The id
|
|
858
|
+
// built here is for an in-memory `SlackConversationContext` map keyed
|
|
859
|
+
// by a broader sanitized form (any non-alphanumeric → `-`) and may
|
|
860
|
+
// diverge from the chat-v2 channel id by design. Do not "unify" —
|
|
861
|
+
// see the 2026-05-15 review of PR #547 for the discussion.
|
|
862
|
+
const conversationId = `slack-${channelId}-${threadTs}`.replace(/[^A-Za-z0-9_-]/g, '-');
|
|
863
|
+
let context = this.conversationContexts.get(key);
|
|
864
|
+
if (!context) {
|
|
865
|
+
context = {
|
|
866
|
+
threadTs,
|
|
867
|
+
channelId,
|
|
868
|
+
userId,
|
|
869
|
+
conversationId,
|
|
870
|
+
startedAt: new Date().toISOString(),
|
|
871
|
+
lastActivityAt: new Date().toISOString(),
|
|
872
|
+
messageCount: 0,
|
|
873
|
+
};
|
|
874
|
+
this.conversationContexts.set(key, context);
|
|
875
|
+
}
|
|
876
|
+
else if (context.conversationId !== conversationId) {
|
|
877
|
+
context.conversationId = conversationId;
|
|
878
|
+
}
|
|
879
|
+
context.lastActivityAt = new Date().toISOString();
|
|
880
|
+
context.messageCount++;
|
|
881
|
+
return context;
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Get service status
|
|
885
|
+
*
|
|
886
|
+
* @returns Current service status
|
|
887
|
+
*/
|
|
888
|
+
getStatus() {
|
|
889
|
+
return { ...this.status };
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Check if service is connected
|
|
893
|
+
*
|
|
894
|
+
* @returns True if connected
|
|
895
|
+
*/
|
|
896
|
+
isConnected() {
|
|
897
|
+
return this.status.connected;
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Get the bot token used to initialize the Slack client.
|
|
901
|
+
* Required for downloading private files from Slack.
|
|
902
|
+
*
|
|
903
|
+
* @returns The bot token string, or null if not initialized
|
|
904
|
+
*/
|
|
905
|
+
getBotToken() {
|
|
906
|
+
return this.config?.botToken || null;
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Upload an image file to a Slack channel.
|
|
910
|
+
*
|
|
911
|
+
* Delegates to the shared upload-with-retry logic.
|
|
912
|
+
* Kept as a separate public method for backward compatibility
|
|
913
|
+
* with callers that use the image-specific endpoint.
|
|
914
|
+
*
|
|
915
|
+
* @param options - Upload configuration
|
|
916
|
+
* @returns Object with the uploaded file ID
|
|
917
|
+
* @throws Error if the client is not initialized or upload fails
|
|
918
|
+
*/
|
|
919
|
+
async uploadImage(options) {
|
|
920
|
+
return this.uploadWithRetry(options, SLACK_IMAGE_CONSTANTS);
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Upload any supported file to a Slack channel.
|
|
924
|
+
*
|
|
925
|
+
* Delegates to the shared upload-with-retry logic.
|
|
926
|
+
* Works for PDFs, images, documents, and other file types supported by Slack.
|
|
927
|
+
*
|
|
928
|
+
* @param options - Upload configuration
|
|
929
|
+
* @returns Object with the uploaded file ID
|
|
930
|
+
* @throws Error if the client is not initialized or upload fails
|
|
931
|
+
*/
|
|
932
|
+
async uploadFile(options) {
|
|
933
|
+
return this.uploadWithRetry(options, SLACK_FILE_UPLOAD_CONSTANTS);
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Shared upload logic with retry/backoff for Slack rate limits.
|
|
937
|
+
*
|
|
938
|
+
* Reads the file from disk and uploads via files.uploadV2.
|
|
939
|
+
* Retries on 429 responses with exponential backoff capped at 60s.
|
|
940
|
+
*
|
|
941
|
+
* @param options - Upload configuration (channel, path, filename, etc.)
|
|
942
|
+
* @param constants - Constants object providing UPLOAD_MAX_RETRIES and UPLOAD_DEFAULT_BACKOFF_MS
|
|
943
|
+
* @returns Object with the uploaded file ID
|
|
944
|
+
* @throws Error if the client is not initialized, retries exhausted, or a non-rate-limit error occurs
|
|
945
|
+
*/
|
|
946
|
+
async uploadWithRetry(options, constants) {
|
|
947
|
+
if (!this.client) {
|
|
948
|
+
throw new Error('Slack client not initialized');
|
|
949
|
+
}
|
|
950
|
+
const filename = options.filename || basename(options.filePath);
|
|
951
|
+
const maxRetries = constants.UPLOAD_MAX_RETRIES;
|
|
952
|
+
const MAX_BACKOFF_MS = 60_000;
|
|
953
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
954
|
+
const fileStream = createReadStream(options.filePath);
|
|
955
|
+
try {
|
|
956
|
+
const result = await this.client.files.uploadV2({
|
|
957
|
+
channel_id: options.channelId,
|
|
958
|
+
file: fileStream,
|
|
959
|
+
filename,
|
|
960
|
+
title: options.title,
|
|
961
|
+
initial_comment: options.initialComment,
|
|
962
|
+
thread_ts: options.threadTs,
|
|
963
|
+
});
|
|
964
|
+
this.status.messagesSent++;
|
|
965
|
+
return { fileId: result.files?.[0]?.id };
|
|
966
|
+
}
|
|
967
|
+
catch (error) {
|
|
968
|
+
fileStream.destroy();
|
|
969
|
+
const isRateLimit = this.isRateLimitError(error);
|
|
970
|
+
if (isRateLimit && attempt < maxRetries) {
|
|
971
|
+
const rawMs = this.extractRetryAfterMs(error) || constants.UPLOAD_DEFAULT_BACKOFF_MS;
|
|
972
|
+
const retryAfterMs = Math.min(rawMs, MAX_BACKOFF_MS);
|
|
973
|
+
this.logger.warn('Upload rate-limited (429), retrying', { retryAfterMs, attempt: attempt + 1, maxRetries });
|
|
974
|
+
await new Promise(resolve => setTimeout(resolve, retryAfterMs));
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
this.logger.error('Upload error', { error: error instanceof Error ? error.message : String(error) });
|
|
978
|
+
throw error;
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
throw new Error('Upload failed after maximum retries');
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Check if an error is a Slack 429 rate limit error.
|
|
985
|
+
*
|
|
986
|
+
* @param error - The caught error
|
|
987
|
+
* @returns True if the error represents a 429 rate limit response
|
|
988
|
+
*/
|
|
989
|
+
isRateLimitError(error) {
|
|
990
|
+
if (error && typeof error === 'object') {
|
|
991
|
+
const err = error;
|
|
992
|
+
// @slack/web-api throws errors with code 'slack_webapi_rate_limited_error'
|
|
993
|
+
if (err.code === 'slack_webapi_rate_limited_error')
|
|
994
|
+
return true;
|
|
995
|
+
// Also check for status 429 in case of raw HTTP errors
|
|
996
|
+
if (err.statusCode === 429 || err.status === 429)
|
|
997
|
+
return true;
|
|
998
|
+
}
|
|
999
|
+
return false;
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Extract the retry-after delay from a Slack rate limit error.
|
|
1003
|
+
*
|
|
1004
|
+
* @param error - The caught error
|
|
1005
|
+
* @returns Retry delay in milliseconds, or null if not found
|
|
1006
|
+
*/
|
|
1007
|
+
extractRetryAfterMs(error) {
|
|
1008
|
+
if (error && typeof error === 'object') {
|
|
1009
|
+
const err = error;
|
|
1010
|
+
// @slack/web-api attaches retryAfter (seconds) to the error
|
|
1011
|
+
if (typeof err.retryAfter === 'number') {
|
|
1012
|
+
return err.retryAfter * 1000;
|
|
1013
|
+
}
|
|
1014
|
+
// Check headers in case of raw response
|
|
1015
|
+
const headers = err.headers;
|
|
1016
|
+
if (headers?.['retry-after']) {
|
|
1017
|
+
const seconds = parseInt(headers['retry-after'], 10);
|
|
1018
|
+
if (!isNaN(seconds))
|
|
1019
|
+
return seconds * 1000;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
return null;
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Disconnect from Slack
|
|
1026
|
+
*/
|
|
1027
|
+
async disconnect() {
|
|
1028
|
+
this.intentionalDisconnect = true;
|
|
1029
|
+
this.cancelReconnectGrace();
|
|
1030
|
+
this.stopHealthCheck();
|
|
1031
|
+
if (this.app) {
|
|
1032
|
+
await this.app.stop();
|
|
1033
|
+
this.status.connected = false;
|
|
1034
|
+
this.emit('disconnected');
|
|
1035
|
+
this.logger.info('Disconnected');
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Get file info from Slack via the files.info API.
|
|
1040
|
+
* Requires the bot token to have `files:read` scope.
|
|
1041
|
+
*
|
|
1042
|
+
* @param fileId - Slack file ID (e.g., F0123ABC456)
|
|
1043
|
+
* @returns Object with private download URLs
|
|
1044
|
+
* @throws Error if the client is not initialized or API call fails
|
|
1045
|
+
*/
|
|
1046
|
+
async getFileInfo(fileId) {
|
|
1047
|
+
if (!this.client) {
|
|
1048
|
+
throw new Error('Slack client not initialized');
|
|
1049
|
+
}
|
|
1050
|
+
const result = await this.client.files.info({ file: fileId });
|
|
1051
|
+
return {
|
|
1052
|
+
url_private: result.file?.url_private || '',
|
|
1053
|
+
url_private_download: result.file?.url_private_download || '',
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Get user info from Slack
|
|
1058
|
+
*
|
|
1059
|
+
* @param userId - Slack user ID
|
|
1060
|
+
* @returns User info object
|
|
1061
|
+
*/
|
|
1062
|
+
async getUserInfo(userId) {
|
|
1063
|
+
if (!this.client) {
|
|
1064
|
+
throw new Error('Slack client not initialized');
|
|
1065
|
+
}
|
|
1066
|
+
const result = await this.client.users.info({ user: userId });
|
|
1067
|
+
return {
|
|
1068
|
+
name: result.user?.name || userId,
|
|
1069
|
+
realName: result.user?.real_name || userId,
|
|
1070
|
+
email: result.user?.profile?.email,
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Get the SlackService singleton instance
|
|
1076
|
+
*
|
|
1077
|
+
* @returns SlackService instance
|
|
1078
|
+
*/
|
|
1079
|
+
export function getSlackService() {
|
|
1080
|
+
if (!slackServiceInstance) {
|
|
1081
|
+
slackServiceInstance = new SlackService();
|
|
1082
|
+
}
|
|
1083
|
+
return slackServiceInstance;
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Reset the SlackService instance (for testing)
|
|
1087
|
+
*/
|
|
1088
|
+
export function resetSlackService() {
|
|
1089
|
+
if (slackServiceInstance) {
|
|
1090
|
+
slackServiceInstance.disconnect().catch(() => { });
|
|
1091
|
+
slackServiceInstance = null;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
//# sourceMappingURL=slack.service.js.map
|